Span<T>和ValueTuple<T>性能是.Net Core非常关键的特性
Span<T>和ValueTuple<T>
性能是.Net Core一个非常关键的特性,今天我们重点研究一下ValueTuple<T>和Span<T>.
一、方法的多个返回值的实现,看ValueTuple<T>
日常开发中,假如我们一个方法有多个返回值,我们可能会用Out出参,或者使用一个自定义类/匿名类型,或者Tuple<T>.
- Out出参可以使用,但是在编写Async方法时不支持。
- 自定义类/匿名类型,需要我们根据返回值的结构,自定义一个类型,带来性能开销,同时增加了编码工作量,同时需要考虑跨域序列化的问题。
- .Net Framework 4.0后引入了Tuple<T>元组,但是Item1,Item2,...不够友好,方法调用方需要了解分别代表的含义。
现在我们看看ValueTuple<T>的实现
C# 7支持返回多个值的语言特性,我们写两个示例代码Tuple<T>和ValueTuple<T>,对比一下:

1 /// <summary>
2 /// Tuple
3 /// </summary>
4 /// <returns></returns>
5 private Tuple<string, List<int>> GetValues()
6 {
7 return new Tuple<string, List<int>>("C7", new List<int> { 1, 2, 3 });
8 }
9
10 /// <summary>
11 /// ValueTuple
12 /// </summary>
13 /// <returns></returns>
14 private (string, List<int>) GetValuesN()
15 {
16 return ("C7", new List<int> { 1, 2, 3 });
17 }

Tuple的示例中,代码声明了一个Tuple元组,内存在托管堆上统一管理,GC垃圾回收在指定时机下回收。
ValueTuple示例中,编译器生成的代码使用的是ValueTuple,其本身就是一个struct,在栈上创建,这使我们既可以访问这个返回值数据,同时确保在包含的数据结构上不需要做垃圾回收。
我们通过IL Spy看下编译后的代码:

上图可以看到:
第一个方法GetValues,返回class [System.Runtime]System.Tuple`2<string, class [System.Collections]System.Collections.Generic.List`1<int32>>,一个类的实例
第二个方法GetValuesN,返回valuetype [System.Runtime]System.ValueTuple`2<string, class [System.Collections]System.Collections.Generic.List`1<int32>>,一个值类型的实例。
类是在托管堆中分配的 (由 CLR 跟踪和管理,并受垃圾收集的管制,是可变的),而值类型分配在堆栈上 (速度快且较少的开销,是不可变的)。
System.ValueTuple 本身并没有被 CLR 跟踪,它只是作为我们使用的嵌入值的一个简单容器。
ValueTuple<T>作为C#7.0支持方法多返回值,的确在底层实现上考虑了性能表现(内存),同时编码上给我们带来了更愉快的语法特性!
二、从字符串操作看Span<T>
大多数.Net开发场景,只使用到了托管堆(由CLR统一管理),其实.Net 有三种类型的内存可以使用,不过要看具体的使用场景。
- 栈内存:我们通常分配的值类型的内存空间,比如 int, double, bool,……它非常快 (通常在 CPU 的缓存中使用),但大小有限 (通常小于 1 MB)。当然,有些开发人员会使用 stackalloc 关键字添加自定义对象,但要知道它们是有危险性的,因为在任何时间都可能发生 StackOverflowException,使我们的整个应用程序崩溃。
- 非托管内存:没有垃圾收集器的内存空间,必须自己使用像 Marshal.AllocHGlobal 和 Marshal.FreeHGlobal 之类的方法预订和释放内存。
- 托管内存 / 托管堆:通过GC垃圾收集器释放已经不再使用的内存空间,使我们大多数人都过着无忧无虑的程序员生活,很少有内存问题。
上述三种类型的内存,都有各自的优缺点,特点的使用场景。如果我们设计一个兼容支持上述三种类型的Lib,需要分别提供两种实现,一种是支持托管堆的,一种是支持栈和非托管内存的。比如说SubString。
我们先看第一种支持托管推的SubString实现:

1 string Substring(string source, int startIndex, int length)
2 {
3 var result = new char[length];
4 for (var i = 0; i < length; i++)
5 {
6 result[i] = source[startIndex + i];
7 }
8
9 return new string(result);
10 }

上述方法内部声明了新的string对象和字符数组,这无疑带来了内存和CPU消息,实现的并不差,但是也不理想。
继续看第二种支持栈和非托管内存的,使用 char*(是的,一个指针!) 和字符串的长度,并返回类似的指向结果的指针。实现上就有点小复杂了。
此时,我们看.Net Core新引入的System.Memory命名空间下的Span<T>. 首先它是一个值类型 (因此没有被垃圾收集器跟踪),它试图统一对任何底层内存类型的访问。看一下它的内部结构:

// Constructor for internal use only.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Span(ref T ptr, int length)
{
Debug.Assert(length >= 0); _pointer = new ByReference<T>(ref ptr);
_length = length;
} public ref T this[Index index]
{
get
{
// Evaluate the actual index first because it helps performance
int actualIndex = index.GetOffset(_length);
return ref this [actualIndex];
}
}

不管我们是使用字符串、char[] 甚至是未管理的 char* 来创建一个 Span<T>, Span<T> 对象都提供了相同的函数,比如返回索引中的元素。可以把它看作是 T[],其中 T 可以是任何类型的内存。
我们用Span<T>编写一个 Substring() 方法
Span<char> SubString(Span<char> source, int startIndex, int length)
{
return source.Slice(startIndex, length);
}
上述方法不返回源数据的副本,而是返回引用源的子集的 Span<T>,对比第一种SubString实现:没有重复数据,没有复制和复制数据的开销。
总结一下:
.NetCore中通过引入诸如 System.ValueTuple and Span<T> 之类的类型,使. net 开发人员更自然地使用在运行时可用的不同类型的内存,同时避免与它们相关的常见缺陷。这种统一带来了性能提升的同时,也简化了我们日常的编码。
Span<T>和ValueTuple<T>性能是.Net Core非常关键的特性的更多相关文章
- .Net Core技术研究-Span<T>和ValueTuple<T>
性能是.Net Core一个非常关键的特性,今天我们重点研究一下ValueTuple<T>和Span<T>. 一.方法的多个返回值的实现,看ValueTuple<T> ...
- 单核性能强悍,Core i3 这次又要“默秒全”?
单核性能强悍,Core i3 这次又要"默秒全"? 在 Intel 历代酷睿处理器中,定位主流级的 Core i3 一直以超高性价比.低功耗.低发热的特点受到广大用户的青睐,在市场 ...
- .NET/C# 使用 Span 为字符串处理提升性能
.NET Core 2.1 和 C# 7.2 带来了 Span 的原生支持,原本需要使用不安全代码操作的内存块现在可以使用安全的方式来完成.此前在性能和稳定性上需要有所取舍,而现在可以兼得了. 简单的 ...
- 性能是.NET Core的一个关键特性
关键要点1).NET Core是跨平台的,可运行在Windows.Linux.Mac OS X和更多平台上:与.NET相比,发布周期要短得多.大多数.NET Core都是通过NuGet软件包交付的,可 ...
- windows服务器性能监控工具、方法及关键指标
原文:http://www.cnblogs.com/liulun/p/3543777.html 监控方法 推荐使用windows自带的“性能监视器”(老版本的windows叫性能计数器)来监控服务器的 ...
- Oracle12c中SQL性能优化(SQL TUNING)新特性之自动重优化(automatic reoptimization)
Oracle12c中的自动重优化 Oracle12c中的自适应查询优化有一系列不同特点组成.像自适应计划(AdaptivePlans)功能可以在运行时修改执行计划,但并不允许计划中连接顺序的改变.自动 ...
- .Net Core中使用ref和Span<T>提高程序性能
一.前言 其实说到ref,很多同学对它已经有所了解,ref是C# 7.0的一个语言特性,它为开发人员提供了返回本地变量引用和值引用的机制. Span也是建立在ref语法基础上的一个复杂的数据类型,在文 ...
- 实际体验Span<T> 的惊人表现
前言 最近做了一个过滤代码块功能的接口.就是获取一些博客文章做文本处理,然后这些博客文章的代码块太多了,很多重复的代码关键词如果被拿过来处理,那么会对文本的特征表示已经特征选择会有很大的影响.所以需要 ...
- 【5min+】传说中的孪生兄弟? Memory and Span
系列介绍 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net ...
随机推荐
- Bootstrap3基础 warning/active... 表格的状态类(不同的背景色)
内容 参数 OS Windows 10 x64 browser Firefox 65.0.2 framework Bootstrap 3.3.7 editor ...
- jenkins安装部署全过程
基本配置: 1.Linux安装配置jdk环境 1.1.上传到 Linux 服务器:例如: 上传至: cd /usr/local 1.2.解压: rpm -ivh jdk-8u111-linux-x64 ...
- 【常见错误】Quartz常见错误
1.集群之后把其中一个Quartz服务停了,其他的也不接手工作 问题描述 集群之后,A节点执行了大多数任务,B节点大部分时间处于空闲,停掉A节点,B节点也不会接手工作. 解决方式 修改Quartz的配 ...
- 洛谷 P2915 【[USACO08NOV]奶牛混合起来Mixed Up Cows】
类似于n皇后的思想,只要把dfs表示放置情况的数字压缩成一个整数,就能实现记忆化搜索了. 一些有关集合的操作: {i}在集合S内:S&(1<<i)==1: 将{i}加入集合S:S= ...
- jenkins配置QQ邮箱自动发送RF测试构建结果通知邮件
声明:转载请注明出处,谢谢 首先确认QQ邮箱SMTP服务器的地址和端口号.如下图所示,请谨记,JENKINS全局邮箱配置需要使用: 步骤1:开启QQ邮箱的smtp服务:登陆QQ邮箱-设置-账户-开启P ...
- Android测试(四)——内容供应器泄露
内容供应器:用来存储和查询应用程序中的数据或来自电话的数据,所有内容供应器都具有唯一的统一的资源标识符(URI)以便被识别和查询. 内容供应期命名惯例:以content://开始 当Android A ...
- .NET实现IoC
.NET里简易实现IoC 前言 在前面的篇幅中对依赖倒置原则和IoC框架的使用只是做了个简单的介绍,并没有很详细的去演示,可能有的朋友还是区分不了依赖倒置.依赖注入.控制反转这几个名词,或许知道的也只 ...
- 颜色(color)透明
颜色写法: 1.英文单词 backgroud-color:red: 2. #fff background-color:#fff; 3.#fefefe background-color:fefefe; ...
- 【转】 SQL - 生成指定范围内的随机数
DECLARE @Result INT DECLARE @Upper INT DECLARE @Lower INT SET @Lower = 1 SET @Upper = 10 SELECT @Res ...
- sharing-jdbc实现读写分离及分库分表
需求: 分库:按业务线business_id将不同业务线的订单存储在不同的数据库上: 分表:按user_id字段将不同用户的订单存储在不同的表上,为方便直接用非分片字段order_id查询,可使用基因 ...