原文:bit.ly/3wSpO4o
作者:Nikita Starichenko
翻译:精致码农

大家好!今天我想和大家分享几个 .NET 的性能小贴士与基准测试。

我的系统环境:

  • BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.985

  • Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores

  • .NET SDK=5.0.104

我将以百分比的形式提供基准测试结果,其中 100% 是最快的结果。

1用 StringBuilder 拼接字符串

我们知道,字符串 string 是不可变的。因此,每当你拼接字符串时,就会分配一个新的字符串对象,并填充内容,最终被回收。所有这些都有昂贵开销,这就是为什么 StringBuilder 在字符串拼接时总有更好的性能。

基准测试例子:

private static StringBuilder sb = new();[Benchmark]
public void Concat3() => ExecuteConcat(3);
[Benchmark]
public void Concat5() => ExecuteConcat(5);
[Benchmark]
public void Concat10() => ExecuteConcat(10);
[Benchmark]
public void Concat100() => ExecuteConcat(100);
[Benchmark]
public void Concat1000() => ExecuteConcat(1000);[Benchmark]
public void Builder3() => ExecuteBuilder(3);
[Benchmark]
public void Builder5() => ExecuteBuilder(5);
[Benchmark]
public void Builder10() => ExecuteBuilder(10);
[Benchmark]
public void Builder100() => ExecuteBuilder(100);
[Benchmark]
public void Builder1000() => ExecuteBuilder(1000);public void ExecuteConcat(int size)
{string s = "";for (int i = 0; i < size; i++){s += "a";}
}public void ExecuteBuilder(int size)
{sb.Clear();for (int i = 0; i < size; i++){sb.Append("a");}
}

结果:

1. 3 string concatenations - 218% (35.21 ns)
2. 3 StringBuilder concatenations - 100% (16.09 ns)1. 5 string concatenations - 277% (66.99 ns)
2. 5 StringBuilder concatenations - 100% (24.16 ns)1. 10 string concatenations - 379% (160.69 ns)
2. 10 StringBuilder concatenations - 100% (42.37 ns)1. 100 string concatenations - 711% (2,796.63 ns)
2. 100 StringBuilder concatenations - 100% (393.12 ns)1. 1000 string concatenations - 3800% (144,100.46 ns)
2. 1000 StringBuilder concatenations - 100% (3,812.22 ns)

2赋予动态集合初始大小

.NET 提供了很多集合类型,比如 List<T>, Dictionary<T>, 和 HashSet<T>。所有这些集合都有动态的容量,当你添加更多的项目时,它们的大小会自动扩大。

当集合达到其大小限制时,它将分配一个新的更大的内存缓冲区,这意味着要进行额外的开销去分配容量。

基准测试例子:

[Benchmark]
public void ListDynamicCapacity()
{List<int> list = new List<int>();for (int i = 0; i < Size; i++){list.Add(i);}
}
[Benchmark]
public void ListPlannedCapacity()
{List<int> list = new List<int>(Size);for (int i = 0; i < Size; i++){list.Add(i);}
}

在第一个方法中,List 集合使用默认容量初始化,并动态扩大。在第二个方法中,初始容量被设置为它所需要的固定大小。

对于 1000 个项目,其结果是:

1. List Dynamic Capacity - 140% (2.490 us)
2. List Planned Capacity - 100% (1.774 us)

DictionaryHashSet 的测试结果是:

1. Dictionary Dynamic Capacity - 233% (20.314 us)
2. Dictionary Planned Capacity - 100% (8.702 us)1. HashSet Dynamic Capacity - 223% (17.004 us)
2. HashSet Planned Capacity - 100% (7.624 us)

3ArrayPool 用于短时大数组

数组的分配和回收的开销可能是相当昂贵的,高频地执行这些分配会增加 GC 的压力并损害性能。一个优雅的解决方案使用是 System.Buffers.ArrayPool 类,它可以在 NuGet 的 Systems.Buffers 中找到。

这个思想和 ThreadPool 很相似。为数组分配一个共享缓冲区,你可以重复使用,而不需要实际分配和回收它们占用的内存。基本用法是调用 ArrayPool<T>.Shared.Rent(size),这将返回一个常规数组,你可以以任何方式使用它。完成后,调用 ArrayPool<int>.Shared.Return(array) 将缓冲区返回到共享池中。

基准测试例子:

[Benchmark]
public void RegularArray()
{int[] array = new int[ArraySize];
}
[Benchmark]
public void SharedArrayPool()
{var pool = ArrayPool<int>.Shared;int[] array = pool.Rent(ArraySize);pool.Return(array);
}

ArraySize = 1000 的结果:

1. Regular Array - 2270% (440.41 ns)
2. Shared ArrayPool - 100% (19.40 ns)

4结构代替类

当涉及到对象回收时,Struct 有如下几个好处:

  • 当结构类型不是类的一部分时,它们被分配在堆栈中,根本不需要垃圾回收。

  • 当结构是类(或任何引用类型)的一部分时,它们被存储在堆中。在这种情况下,它们是内联存储的,并且会随包含类型回收而回收。内联意味着该结构的数据是按原样存储的,这与引用类型相反,在引用类型中,指针被存储到堆上另一个位置。所以回收的成本要低很多。

  • 结构比引用类型占用的内存更少,因为它们没有 ObjectHeaderMethodTable

基准测试例子:

class VectorClass
{public int X { get; set; }public int Y { get; set; }
}struct VectorStruct
{public int X { get; set; }public int Y { get; set; }
}private const int ITEMS = 10000;[Benchmark]
public void WithClass()
{VectorClass[] vectors = new VectorClass[ITEMS];for (int i = 0; i < ITEMS; i++){vectors[i] = new VectorClass();vectors[i].X = 5;vectors[i].Y = 10;}
}[Benchmark]
public void WithStruct()
{VectorStruct[] vectors = new VectorStruct[ITEMS];// At this point all the vectors instances are already allocated with default valuesfor (int i = 0; i < ITEMS; i++){vectors[i].X = 5;vectors[i].Y = 10;}
}

结果:

1. With Class - 742% (88.83 us)
2. With Struct - 100% (11.97 us)

5ConcurrentQueue<T> 代替 ConcurrentBag<T>

在没有基准测试的情况下,不要使用 ConcurrentBag<T>。这个集合是为非常特殊的使用场景而设计的(当经常有项目被排队的线程删除时)。如果需要一个并发的集合队列,请选择 ConcurrentQueue<T>

基准测试例子:

private static int Size = 1000;[Benchmark]
public void Bag()
{ConcurrentBag<int> bag = new();for (int i = 0; i < Size; i++){bag.Add(i);}
}[Benchmark]
public void Queue()
{ConcurrentQueue<int> bag = new();for (int i = 0; i < Size; i++){bag.Enqueue(i);}
}

结果:

1. ConcurrentBag - 165% (24.21 us)
2. ConcurrentQueue - 100% (14.64 us)

感谢大家阅读!

五个 .NET 性能小贴士相关推荐

  1. 夏日“轻”凉小贴士,华为FreeBuds 4全给你

    今夏炎热的天气,就这么突然到来了~高考刚过,很多同学在享受夏日阳光的同时,也会觉得能得到一丝清凉,是对自己最大的宠爱.除了吃冰.游泳外,燥热的环境中能独享一分清凉,也是疗愈身心最好的方式.这不,华为新 ...

  2. 掌握这 25 条小贴士,快速提升数据可视化能力!

    英文:Katy French,翻译:优设 可视化不是单纯的数据展示,其真正价值是设计出可以被读者轻松理解的数据展示.设计过程中的每一个选择,最终都应落地于读者的体验,而非设计者个人. 本文提到了一些常 ...

  3. 配置高性能ElasticSearch集群的9个小贴士

    Loggly服务底层的很多核心功能都使用了ElasticSearch作为搜索引擎.就像Jon Gifford(译者注:Loggly博客作者之一)在他近期关于"ElasticSearch vs ...

  4. netbeans6.8_NetBeans 8.0的五个新性能提示

    netbeans6.8 NetBeans 8.0引入了几个新的Java提示 . 尽管有很多与Java Persistence API相关的新提示,但我还是关注Performance类别中的五个新提示. ...

  5. NetBeans 8.0的五个新性能提示

    NetBeans 8.0引入了几个新的Java提示 . 尽管有许多与Java Persistence API相关的新提示,但我还是关注Performance类别中的五个新提示. NetBeans 8. ...

  6. Spring Cloud实战小贴士:Zuul处理Cookie和重定向

    由于我们在之前所有的入门教程中,对于HTTP请求都采用了简单的接口实现.而实际使用过程中,我们的HTTP请求要复杂的多,比如当我们将Spring Cloud Zuul作为API网关接入网站类应用时,往 ...

  7. Spring Cloud实战小贴士:Zuul统一异常处理(一)

    在上一篇<Spring Cloud源码分析(四)Zuul:核心过滤器>一文中,我们详细介绍了Spring Cloud Zuul中自己实现的一些核心过滤器,以及这些过滤器在请求生命周期中的不 ...

  8. Spring Cloud实战小贴士:Zuul统一异常处理(二)

    在前几天发布的<Spring Cloud实战小贴士:Zuul统一异常处理(一)>一文中,我们详细说明了当Zuul的过滤器中抛出异常时会发生客户端没有返回任何内容的问题以及针对这个问题的两种 ...

  9. 新买的笔记本电脑怎么分盘_别再吐槽游戏本越玩越慢:笔记本电脑降速揭秘+散热维护小贴士...

    花大价钱买的电竞本,配置越高?降速越狠? 夏天到了,日益升高的温度不会影响我们日常搞机的热情~ 新买的4核.6核.8核游戏本照样在各大游戏中征战四方. 然而,为什么明明配置很高的笔记本电脑,突然变得如 ...

最新文章

  1. 2018 ACM-ICPC Asia Shenyang Regional Contest 题解(9 / 13)【每日亿题2021/2/24】
  2. python下载不下来_Python不能下载zip文件,而Browser/Selenium可以下载,这是为什么?...
  3. AIX 中关于文件存储的相关命令
  4. Unity学习笔记2 简易2D横版RPG游戏制作(二)
  5. NEO versus Ethereum: Why NEO might be 2018’s strongest cryptocurrency
  6. php 网页截屏,怎么用PHP实现网页截图
  7. 如何查看SAP云平台哪些地域支持哪些服务
  8. Codeforces 258B Little Elephant and Elections
  9. 腾讯和阿里巴巴考虑互相开放生态,是真开放还是新的垄断?
  10. ModelState用法
  11. Android笔记 notification
  12. DBeaver执行SQL脚本
  13. 探索将scratch3移植到树莓派
  14. 《数据挖掘概念与技术》第三版 范明 孟小峰译 课后习题答案(一)
  15. matlab图像粘连计数,Matlab图像处理在水稻谷粒计数中的应用
  16. 手机上怎么换证件照底色?手机一键照片改背景颜色
  17. 世界上最伟大最邪恶的软件发明
  18. 河钢唐钢微尔云计算中心对外签订首个合同
  19. 走进微软亚洲研究院随笔
  20. 虚拟机安装和优盘启动盘制作

热门文章

  1. 伍迷随想冷饭集 之 瞻前顾后之随想
  2. 蓝桥杯——快速排序(2018JavaB组第5题9分)
  3. Generative Adversarial Learning Towards Fast Weakly Supervised Detection(CVPR2018)阅读笔记
  4. 如何查找Power BI本地报表服务器产品密钥
  5. 大型网站技术架构:核心原理与案例分析笔记
  6. Hbase笔记4 java操作Hbase
  7. Appium同时运行多个设备
  8. js浏览器对象模型(BOM)
  9. 【NOIp 2015】【DFS】斗地主
  10. DateTimeToUnix/UnixToDateTime 对接时间转换