架构师_程序员_码农网

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2060|回复: 2

[资料] 【转】.NET性能优化-推荐使用Collections.Pooled

[复制链接]
发表于 2022-5-29 13:45:22 | 显示全部楼层 |阅读模式
简介

性能优化就是如何在保证处理相同数量的请求情况下占用更少的资源,而这个资源一般就是CPU或者内存,当然还有操作系统IO句柄、网络流量、磁盘占用等等。但是绝大多数时候,我们就是在降低CPU和内存的占用率。
之前分享的内容都有一些局限性,很难直接改造,今天要和大家分享一个简单的方法,只需要替换几个集合类型,就可以达到提升性能和降低内存占用的效果。
今天要给大家分享一个类库,这个类库叫 Collections.Pooled,从名字就可以看出来,它是通过池化内存来达到降低内存占用和GC的目的,后面我们会直接来看看它的性能到底怎么样,另外也会带大家看看源码,为什么它会带来这些性能提升。

Collections.Pooled

项目链接:https://github.com/jtmueller/Collections.Pooled

该库基于System.Collections.Generic中的类,这些类已经被修改,以利用新的System.Span<T>和System.Buffers.ArrayPool<T>类库,达到减少内存分配,提高性能,并允许与现代API的更大的互操作性的目的。
Collections.Pooled支持.NETStandard2.0(.NET Framework 4.6.1+),以及针对.NET Core 2.1+的优化构建。一套广泛的单元测试和基准已经从corefx移植过来。

测试总数:27501。通过:27501。失败:0。跳过:0。
测试运行成功。
测试执行时间:9.9019秒

如何使用

通过Nuget就可以很简单的安装这个类库,NuGet Version 。

在 Collections.Pooled 类库中,它针对我们常使用的集合类型都实现了池化的版本,和.NET原生类型的对比如下所示。

.NET原生Collections.Pooled备注
List<T>PooledList<T>泛型集合类
Dictionary<TKey, TValue>PooledDictionary<TKey, TValue>泛型字典类
HashSet<T>PooledSet<T>泛型哈希集合类
Stack<T>Stack<T>泛型栈
Queue<T>PooledQueue<T>泛型队列

在使用时,我们只需要将对应的.NET原生版本换成Collections.Pooled版本就可以了,如下方的代码所示:

但是我们需要注意,Pooled类型实现了IDispose接口,它通过Dispose()方法将使用的内存归还到池中,所以我们需要在使用完Pooled集合对象以后调用它的Dispose()方法。或者可以直接使用using var关键字。

注意:使用Collections.Pooled内的集合对象最好需要手动释放掉它,不过不释放也没有关系,GC最终会回收它,只是它不能归还到池中,达不到节省内存的效果了。
由于它会复用内存空间,在将内存空间返回到池中的时候,需要对集合内的元素做处理,它提供了一个叫ClearMode的枚举供使用,定义如下:




默认情况下,使用默认值Auto即可,如果有特殊的性能要求,知晓风险后可以使用Never。
对于引用类型和包含引用类型的值类型,我们必须在将内存空间归还到池的时候清空数组引用,如果不清除会导致GC无法释放这部分内存空间(因为元素的引用一直被池持有),如果是纯值类型,那么就可以不清空,在使用结构体替代类这篇文章中,我描述了引用类型和结构体(值类型)数组的存储区别,纯值类型没有对象头回收也无需GC介入。


.NET性能优化-使用结构体替代类:https://www.cnblogs.com/InCerry/p/Dotnet-Opt-Perf-Use-Struct-Instead-Of-Class.html

性能对比

我没有单独做Benchmark,直接使用的开源项目的跑分结果,很多项目的内存占用都是0,那是因为使用的池化的内存,没有多余的分配。

PooledList<T>

在Benchmark中循环向集合添加2048个元素,.NET原生的List<T>需要110us(根据实际跑分结果,图中的毫秒应该是笔误)和263KB内存,而PooledList<T>只需要36us和0KB内存。


QQ截图20220529134003.jpg

PooledDictionary<TKey, TValue>

在Benchmark中循环向字典添加10_0000个元素,.NET原生的Dictionary<TKey, TValue>需要11ms和13MB内存,而PooledDictionary<TKey, TValue>只需要7ms和0MB内存。


QQ截图20220529134035.jpg

PooledSet<T>

在Benchmark中循环向哈希集合添加10_0000个元素,.NET原生的HashSet<T>需要5348ms和2MB,而PooledSet<T>只需要4723ms和0MB内存。


QQ截图20220529134100.jpg

PooledStack<T>

在Benchmark中循环向栈添加10_0000个元素,.NET原生的PooledStack<T>需要1079ms和2MB,而PooledStack<T>只需要633ms和0MB内存。


QQ截图20220529134126.jpg

PooledQueue<T>

在Benchmark中循环向队列添加10_0000个元素,.NET原生的PooledQueue<T>需要681ms和1MB,而PooledQueue<T>只需要408ms和0MB内存。


QQ截图20220529134149.jpg

未手动释放场景

另外在上文中我们提到了Pooled的集合类型需要释放,但是不释放也没有太大的关系,因为GC会去回收。


Benchmark结果如下:

QQ截图20220529134235.jpg

可以从上面的Benchmark结果可以得出结论。

及时释放Pooled类型集合几乎不会触发GC和分配内存,从上图中它只分配了56Byte内存。
就算不释放Pooled类型集合,因为它从池中分配内存,在进行ReSize扩容操作时还是会复用内存,另外跳过了GC分配内存初始化步骤,速度也比较快。
最慢的就是使用普通集合类型,每次ReSize扩容操作都需要申请新的内存空间,GC也要回收之前的内存空间。


原理解析

如果大家看过我之前的博文你应该为集合类型设置初始大小和浅析C# Dictionary实现原理就可以知道,.NET BCL开发人员为了高性能的随机访问,这些基本集合类型的底层数据结构都是数组,我们以List<T>为例。

创建新的数组来存储添加进来的元素。
如果数组空间不够,那么就触发扩容操作,申请2倍的空间大小。
构造函数代码如下,可以看到是直接创建的泛型数组:


那么如果想要池化内存,只需要把类库中使用new关键字申请的地方,改为使用池化的申请。这里和大家分享.NET BCL中的一个类型,叫ArrayPool,它提供了可重复使用的泛型实例的数组资源池,使用它可以降低对GC的压力,在频繁创建和销毁数组的情况下提升性能。

而我们Pooled类型的底层就是使用ArrayPool来共享资源池,从它的构造函数中,我们可以看到它默认使用的是ArrayPool<T>.Shared来分配数组对象,当然你也可以创建自己的ArrayPool来让它使用。


另外在进行容量调整操作(扩容)时,会将旧的数组归还回线程池,新的数组也在池中获取。

另外作者使用了Span优化了Add、Insert等等API,让它们有更好的随机访问性能;另外还加入了TryXXX系列API,可以更方便的方式的使用它。比如List<T>类相比PooledList<T>就有多达170个修改。

QQ截图20220529134445.jpg

总结

在我们线上实际的使用过程中,完全可以用Pooled提供的集合类型替代原生的集合类型,对降低内存占用率和P95延时有非常大的帮助。
另外就算忘记释放了,那性能也不会比使用原生的集合类型差多少。当然最好的习惯就是及时的释放它。


原文:https://www.cnblogs.com/InCerry/p/Recommand_Use_Collections_Pooled.html




上一篇:【转】RecyclableMemoryStream提供高性能的.NET流
下一篇:【实战】服务器搭建 LibreSpeed 测试网速
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
发表于 2022-5-29 17:12:36 | 显示全部楼层
学习一下
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
发表于 2022-6-20 09:09:22 | 显示全部楼层
学习一下混合
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

免责声明:
码农网所发布的一切软件、编程资料或者文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:help@itsvse.com

QQ|手机版|小黑屋|架构师 ( 鲁ICP备14021824号-2 )|网站地图

GMT+8, 2024-3-29 01:20

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表