【5min+】闪电光速拳? .NetCore 中的Span

系列介绍

简介

【五分钟的DotNet】是一个利用您的碎片化时间来学习和丰富.net知识的博文系列。它所包含了.net体系中可能会涉及到的方方面面,好比C#的小细节,AspnetCore,微服务中的.net知识等等。
5min+不是超过5分钟的意思,"+"是知识的增长。so,它是让您花费5分钟如下的时间来提高您的知识储备量。html

正文

在dotnet core2.x以后,引入了一个叫作Span<T>的类型。若是您的项目已经升级到了新版的dotnet core 以及使用C# 7+。您会发现咱们曾经使用的许许多多类型都增长了一个扩展方法“AsSpan()”。在Vs中小手一点就会出现:c#

var s = ("xxx").AsSpan();
var s1 = new byte[10].AsSpan();
//.......more

那么这个家伙究竟是个什么东西?怎么用呢?数组

先来扒一扒它的内部方法:安全

public readonly ref struct Span<T>
{
    public void Clear();
    public void CopyTo([NullableAttribute(new[] { 0, 1 })] Span<T> destination);
    public void Fill(T value);
    public Enumerator GetEnumerator();
    public Span<T> Slice(int start, int length);
    public T[] ToArray();
    public override string ToString();

    //.....
}

这里只展现它部分的方法,可是关键的一点咱们能够看到:它是一个结构性(struct 关键字)。异步

并且!!并且!!! 你没看错,它还加了一个ref关键字。ide

因此按照咱们在上一篇文章中介绍过的 .net中的栈和堆,咱们猜测这种结构类型的数据应该是存放在内存栈中,具备很快的访问速度。并且它拥有了ref关键字,证实它具备ref结构体的特色:函数

  • 不能对 ref struct 装箱
  • ref struct 类型不能实现接口
  • 不能将 ref struct 声明为类或常规结构的字段成员
  • 不能声明异步方法中属于 ref struct 类型的本地变量
  • 没法在迭代器中声明 ref struct 本地变量
  • 没法捕获 Lambda 表达式或本地函数中的 ref struct 变量

并且根据它公开的这些方法,咱们会发现它有点相似咱们经常使用的几个基础类型:string 、 byte[] ……。微服务

因此直觉告诉咱们,它应该是一个拿来存放数据的类型。性能

so,来看看MSDN - Magazine中它的解释:学习

System.Span<T> 是在 .NET 中发挥关键做用的新值类型。使用它,能够表示任意内存的相邻区域,不管相应内存是与托管对象相关联,仍是经过互操做由本机代码提供,亦或是位于堆栈上。除了具备上述用途外,它仍能确保安全访问和高性能特性,就像数组同样。

果不其然,和咱们猜测的同样。那么它出现的意义是什么呢? 性能!!!!

并且是超级快的性能。你们都知道以往若是咱们想提升数据间的操做效率(好比数据偏移、裁剪等),就只能使用指针来操做内存中的数据。这样虽然一波操做猛如虎,可是写起来费劲不说,咱们还得将传统的C#代码设置为不安全代码,除了添加unsafe关键字以外还须要打开项目中执行不安全代码的选项。

因此,有没有办法既不操做指针而又有高性能呢? 好吧,Span大爷来了。

Span在C# 7.x中被引入,因此它的年龄还算比较小,也是由于这些缘由。以往的项目可能没有办法使用它。

它到底有多快

你们通常都是想直接看东西,因此我写了一份对比的代码。功能很简单,都是截取字符串中的一部分代码,而且进行屡次的循环操做。

执行结果我都惊呆了:

x

是的,您没有看错。差距不是通常的大。

其实刚开始我觉得Span并无什么做用,由于我将数据源(图中的compareStr)仅仅设置为了几个单词。而后对他们进行了1亿的循环操做,可是最后的结果只有很小的差距,不到百分之30。

后来我想了一下,应该让数据更贴近现实,因而就将一张图片转换为base64而后做为数据源。结果惊呆了,差了接近百倍。并且随着循环次数和对数据源的操做次数的增多,Span和传统字符串之间的性能差距更大。

传说中的闪电光速拳到底有多快呢

x

它为何这么快

它与传统的string操做比起来为何会具备这么快的速度呢? 按照咱们以前的一些猜测和msdn所给出的一点信息,咱们能够获得如下的结论:

  • 它分配堆栈上而不是在托管堆。
  • 它所建立的数据是内存连续的,所以具备更快的遍历速度。

这些特色和string等原有类型比起来就很是的具备优点了:原来对string操做涉及到大量的字符串分配和内存复制。因此当操做的数据量小的时候还好,可是随着操做次数和处理数据量的增长以后,这是很是消耗性能的。

x

Span会给咱们带来什么

那么,既然它拥有如此高的性能,那么咱们该在什么地方使用它呢?

这很简单,若是您之前有对大量字符串进行截取或者处理的地方,通常均可以替换为Span。(为何是通常呢😏)

除了string能够转换为span以外,其它的byte[],char[]等等均可以转换为span进行操做。因此这是很是值得高兴的一件事情,它会为咱们数据处理带来显著的性能提高。好比字节流缓冲,视频流的处理,数据的加密解密等等操做均可以使用Span来完成了。

so,在如今的.NETCore runtime中,您会发现大量的类中都开始使用了Span。

x

并且,Span为咱们实现了ExplicitImplicit,因此咱们能够直接将支持的数组类型赋值给Span: (若是您不了解这两个关键字:戳这儿)

var arr = new byte[10];
Span<byte> bytes = arr; // 直接将byte[]赋值给Span

心动了吗?了解如下Span,而且尝试着使用它吧。

可是,请注意!! Span也是具备缺点的:由于只能存放在内存栈中,因此它不具备线程安全,它没法跨异步操做。还有它ref结构的缘由,没法装箱拆箱等。

那么若是咱们须要跨线程共享数据,又想拥有高性能怎么办呢? 别急,下一期我们再来谈。😜

最后,小声说一句:创做不易,点个推荐吧😇

相关文章
相关标签/搜索