系列介紹 簡介 【五分鐘的DotNet】是一個利用您的碎片化時間來學習和豐富.net知識的博文系列。它所包含了.net體系中可能會涉及到的方方面面,比如C 的小細節,AspnetCore,微服務中的.net知識等等。 5min+不是超過5分鐘的意思,"+"是知識的增加。so,它是讓您花費5分鐘以下的 ...
系列介紹
簡介
【五分鐘的DotNet】是一個利用您的碎片化時間來學習和豐富.net知識的博文系列。它所包含了.net體系中可能會涉及到的方方面面,比如C#的小細節,AspnetCore,微服務中的.net知識等等。
5min+不是超過5分鐘的意思,"+"是知識的增加。so,它是讓您花費5分鐘以下的時間來提升您的知識儲備量。
正文
在dotnet core2.x之後,引入了一個叫做Span<T>的類型。如果您的項目已經升級到了新版的dotnet core 以及使用C# 7+。您會發現我們曾經使用的許許多多類型都增加了一個擴展方法“AsSpan()”。在Vs中小手一點就會出現:
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關鍵字。
所以按照我們在上一篇文章中介紹過的 .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中被引入,所以它的年齡還算比較小,也是因為這些原因。以往的項目可能沒有辦法使用它。
它到底有多快
大家一般都是想直接看東西,所以我寫了一份對比的代碼。功能很簡單,都是截取字元串中的一部分代碼,並且進行多次的迴圈操作。
執行結果我都驚呆了:
是的,您沒有看錯。差距不是一般的大。
其實剛開始我以為Span並沒有什麼作用,因為我將數據源(圖中的compareStr)僅僅設置為了幾個單詞。然後對他們進行了1億的迴圈操作,但是最後的結果只有很小的差距,不到百分之30。
後來我想了一下,應該讓數據更貼近現實,於是就將一張圖片轉換為base64然後作為數據源。結果驚呆了,差了接近百倍。而且隨著迴圈次數和對數據源的操作次數的增多,Span和傳統字元串之間的性能差距更大。
傳說中的閃電光速拳到底有多快呢
它為什麼這麼快
它與傳統的string操作比起來為什麼會具有這麼快的速度呢? 按照我們之前的一些猜想和msdn所給出的一點信息,我們可以得到以下的結論:
- 它分配堆棧上而不是在托管堆。
- 它所創建的數據是記憶體連續的,因此具有更快的遍歷速度。
這些特點和string等原有類型比起來就非常的具有優勢了:原來對string操作涉及到大量的字元串分配和記憶體複製。所以當操作的數據量小的時候還好,但是隨著操作次數和處理數據量的增加之後,這是非常消耗性能的。
Span會給我們帶來什麼
那麼,既然它擁有如此高的性能,那麼我們該在什麼地方使用它呢?
這很簡單,如果您以前有對大量字元串進行截取或者處理的地方,一般都可以替換為Span。(為什麼是一般呢