【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結構的緣由,沒法裝箱拆箱等。

那麼若是咱們須要跨線程共享數據,又想擁有高性能怎麼辦呢? 別急,下一期我們再來談。😜

最後,小聲說一句:創做不易,點個推薦吧😇

相關文章
相關標籤/搜索