站在前人的肩膀上重新透視C# SpanT數據結構

34f2b5114f312ec615b55939f93bb936.gif

? ? 先談一下我對Span的看法, Span是指向任意連續內存空間的類型安全、內存安全的視圖,可操作的滑動窗口。

Span和Memory都是包裝了可以在pipeline上使用的結構化數據的內存緩沖器,他們被設計用于在pipeline中高效傳遞數據。

定語解讀

這里面許多定語,值得我們細細揣摩:

  1. 1. 指向任意連續內存空間:支持托管堆,原生內存、堆棧, 這個可從Span

    的幾個重載構造函數窺視一二。
  2. 2. 類型安全:Span?是一個泛型。

  3. 3. 內存安全:?Span[1]是一個readonly ref struct數據結構,用于表征一段連續內存的關鍵屬性被設置成只讀readonly, 保證了所有的操作只能在這段內存內。

//?截取自Span源碼?
public?readonly?ref?struct?Span<T>
{//?表征一段連續內存的關鍵屬性?Pointer?&?Length?都只能從構造函數賦值///?<summary>A?byref?or?a?native?ptr.</summary>internal?readonly?ByReference<T>?_reference;///?<summary>The?number?of?elements?this?Span?contains.</summary>private?readonly?int?_length;[MethodImpl(MethodImplOptions.AggressiveInlining)]public?Span(T[]??array){if?(array?==?null){this?=?default;return;?//?returns?default}if?(!typeof(T).IsValueType?&&?array.GetType()?!=?typeof(T[]))ThrowHelper.ThrowArrayTypeMismatchException();_reference?=?new?ByReference<T>(ref?MemoryMarshal.GetArrayDataReference(array));_length?=?array.Length;}
}
  1. 4. 視圖:操作結果會直接體現到底層的連續內存。

    e9b1bca58c9347b6ac990a8bbae23f5e.png

至此我們來看一個簡單的用法, 利用span操作指向一段堆棧空間。

static??void??Main(){Span<byte>?arraySpan?=?stackalloc?byte[100];??//?包含指針和Length的只讀指針,?類似于go里面的切片byte?data?=?0;for?(int?ctr?=?0;?ctr?<?arraySpan.Length;?ctr++)arraySpan[ctr]?=?data++;arraySpan.Fill(1);var?arraySum?=?Sum(arraySpan);Console.WriteLine($"The?sum?is?{arraySum}");???//?輸出100arraySpan.Clear();var?slice??=??arraySpan.Slice(0,50);?//?因為是只讀屬性,?內部New?Span<>(),?產生新的切片arraySum?=?Sum(slice);Console.WriteLine($"The?sum?is?{arraySum}");??//?輸出0}[MethodImpl(MethodImplOptions.AggressiveInlining)]static?int??Sum(Span<byte>?array){int?arraySum?=?0;foreach?(var?value?in?array)arraySum?+=?value;return?arraySum;}
  • ??此處Span??指向了特定的堆棧空間, Fill,Clear 等操作的效果直接體現到該段內存。

  • ??注意Slice切片方法,內部實質是產生新的Span,是一個新的視圖,對新span的操作會體現到原始底層數據結構。

  • [MethodImpl(MethodImplOptions.AggressiveInlining)]public?Span<T>?Slice(int?start){if?((uint)start?>?(uint)_length)ThrowHelper.ThrowArgumentOutOfRangeException();return?new?Span<T>(ref?Unsafe.Add(ref?_reference.Value,?(nint)(uint)start?/*?force?zero-extension?*/),?_length?-?start);}
  • ? ? ? 從Slice切片源碼可以看到,實質是利用原ptr & length 產生包含新的ptr & length的操作視圖, ptr其實是指針的移動,也就是定位新的數據塊, 但是終歸是在原始數據塊內部。?

衍生技能點

我們再細看Span的定義, 有幾個關鍵詞建議大家溫故而知新。

1.?readonly strcut[2]

從C#7.2開始,你可以將readonly作用在struct上,指示該struct不可改變

span?被定義為readonly struct,內部屬性自然也是readonly,從上面的分析和實例看我們可以針對Span表征的特定連續內存空間做內容更新操作;
如果想限制更新該連續內存空間的內容, C#提供了ReadOnlySpan<T>類型, 該類型強調該塊內存只讀,也就是不存在Span?擁有的Fill,Clear等方法。

一線碼農大佬寫了文章講述[使用span對字符串求和]的姿勢,大家都說使用span能高效操作內存,我們對該用例BenchmarkDotNet壓測。

using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
using?System.Threading.Tasks;
using?System.Buffers;
using?System.Runtime.CompilerServices;
using?BenchmarkDotNet.Attributes;
using?BenchmarkDotNet.Running;namespace?ConsoleApp3
{public?class?Program{static??void?Main(){var?summary?=?BenchmarkRunner.Run<MemoryBenchmarkerDemo>();}}[MemoryDiagnoser,RankColumn]public?class?MemoryBenchmarkerDemo{int?NumberOfItems?=?100000;//?對字符串切割,?會產生字符串小對象[Benchmark]public?void??StringSplit(){for?(int?i?=?0;?i?<?NumberOfItems;?i++){var?s?=?"97?3";var?arr?=?s.Split(new?string[]?{?"?"?},?StringSplitOptions.RemoveEmptyEntries);var?num1?=?int.Parse(arr[0]);var?num2?=?int.Parse(arr[1]);_?=?num1?+?num2;}}//?對底層字符串切片[Benchmark]public?void?StringSlice(){for?(int?i?=?0;?i?<?NumberOfItems;?i++){var?s?=?"97?3";var?position?=?s.IndexOf('?');ReadOnlySpan<char>?span =?s.AsSpan();var?num1?=?int.Parse(span.Slice(0,?position));var?num2?=?int.Parse(span.Slice(position));_=?num1+?num2;}}}
}

a839de4b74e6907926d8386b15ec577b.png

壓測解讀:

?對字符串運行時切分,不會利用駐留池,于是case1會分配大量小對象;對gc造成壓力。??

?case2對底層字符串切片,雖然會產生不同的透視對象Span, 但是實際引用了的原始內存塊的偏移區間, 不存在分配新內存。

2.?ref struct[3]

從C#7.2開始,ref可以作用在struct,指示該類型被分配在堆棧上,并且不能轉義到托管堆

Span,ReadonlySpan?包裝了對于任意連續內存快的透視操作,但是只能被存儲堆棧上,不適用于一些場景,例如異步調用,.NET Core 2.1為此新增了Memory[4]?, ReadOnlyMemory, 可以被存儲在托管堆上,這個暫時按下不表。

最后用一張圖總結, 本文成文,感謝[?yi念之間?]大佬參與討論。

5aeba6400eb7cb3df0c3c0211e7e3053.png

引用鏈接

[1]?Span:?https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Span.cs
[2]?readonly strcut:?https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct#readonly-struct
[3]?ref struct:?https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct
[4]?Memory:?https://docs.microsoft.com/en-us/dotnet/standard/memory-and-spans/memory-t-usage-guidelines

與本文相關的經典文章

C#語法糖系列 —— 第四篇:聊聊 Span 的底層玩法

非常簡單的string駐留池,你對它真的了解嗎

22b0140495a107bfa25e4a5e193e5d49.gif

年終總結:2021技術文大盤點 ?| ?打包過去,面向未來

項目總結:麻雀雖小,五臟俱全

理念總結:實話實說:只會.NET,會讓我們一直處于鄙視鏈、食物鏈的下游

云原生系列:?什么是云原生?

點“0331d040939fbc7a382ba5407a76f1f5.gif戳“在看cd7dc3fef1509c0d6af10a1ce66db3fe.gif

體現態度很有必要!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/287777.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/287777.shtml
英文地址,請注明出處:http://en.pswp.cn/news/287777.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

集合學習

List集合&#xff1a;ArrayList集合基于動態數組結構&#xff0c;查詢優&#xff0c;LinkedList 基于鏈表結構 數據移動優。是一個有序的隊列集合 set集合&#xff1a;HashSet和TreeSet 。是一個無序不重復集合 Map集合&#xff1a;HashMap和TreeMap。是一個KEY-VALUE映射的集合…

《零基礎看得懂的C語言入門教程 》——(十)C語言的指針原來是這樣

一、學習目標 了解指針的概念了解指針的使用方法了解雙重指針 目錄 C語言真的很難嗎&#xff1f;那是你沒看這張圖&#xff0c;化整為零輕松學習C語言。 第一篇&#xff1a;&#xff08;一&#xff09;脫離學習誤區 第二篇&#xff1a;&#xff08;二&#xff09;C語言沒那…

T-SQL編程基礎之一:變量與基本語句

一個標準的計算機語言,大概要提供的必要主要功能是:變量說明、分支判斷、循環和輸入輸出結果。T-SQL也一樣,具有這些功能,只不過T-SQL的輸入和輸出不是界面,而是表。 完全精確描述一個計算機語言,大概要很厚的書才能做到,好在目前這些書籍的發行也很多,許多書描述的都…

Java之volatile如何保證可見性和指令重排序

1 我們先了解CPU緩存 CPU緩存為了解決CPU運算速度與內存讀寫速度不匹配的問題&#xff0c;因為CPU運算速度要比內存讀寫速度快得多 一次主內存的訪問通常在幾十到幾百個時鐘周期一次L1高速緩存的讀寫只需要1~2個時鐘周期一次L2高速緩存的讀寫也只需要數十個時鐘周期 CPU大多數…

bigpipe提升網站響應速度

2019獨角獸企業重金招聘Python工程師標準>>> 主要思想就是通過異步 發起一次請求&#xff0c;后端不關閉輸出流&#xff0c;多個線程處理各自任務&#xff0c;然后分別發送到客戶端。 https://github.com/4rnold/Demo-Project/tree/master/bigpipe-demohttps://gith…

mysql 添加用戶_mysql創建用戶與授權

一、創建用戶CREATE USER usernamehost IDENTIFIED BY password;說明username&#xff1a;你將創建的用戶名host&#xff1a;指定該用戶在哪個主機上可以登陸&#xff0c;如果是本地用戶可用localhost&#xff0c;如果想讓該用戶可以從任意遠程主機登陸&#xff0c;可以使用通配…

《零基礎看得懂的C語言入門教程 》——(十一)C語言自定義函數真的很簡單

一、學習目標 了解C語言的自定義函數的使用方法了解C語言自定義函數的傳參了解C語言自定義函數的返回值 目錄 C語言真的很難嗎&#xff1f;那是你沒看這張圖&#xff0c;化整為零輕松學習C語言。 第一篇&#xff1a;&#xff08;一&#xff09;脫離學習誤區 第二篇&#xf…

T-SQL編程基礎之二:條件選擇、循環編程

1. 條件判斷以及GOTO語句 條件判斷是計算機語言的重要功能,在T-SQL中,條件判斷的語句是: if 條件 … else … 或者是: if 條件 … 注意寫法和C類似,但條件描述不使用()也可以。如果是在一個條件里執行多條語句,則要構造復合語句,復合語句是在BEGIN…EDN中構造…

**【ci框架】精通CodeIgniter框架

http://blog.csdn.net/yanhui_wei/article/details/25803945 一、大綱 [php] view plaincopy1、codeigniter框架的授課內容安排 2、codeigniter框架的簡介 |-----關于框架的概念 |-----使用CI框架的好處 |-----為什么選擇CI框架 3、codeigniter框架…

AspNetCore開源中間件-VueRouterHistory

前言用過VueRouter路由組件的應該都知道&#xff0c;VueRouter有hash和history兩種模式。hash模式會在url中插入#&#xff0c;history模式下url則看上去更加簡潔美觀。如果想要支持history模式則必須要后端服務進行配合。常用后端服務器配置方式請參考 后端配置例子后端配置例子…

T-SQL編程基礎之三:游標(Cursor)編程

SQL是一種面向集合操作的語言,大多情況下,一個SQL語句將會操作數據庫表里的很多數據,基本上,一個數據庫的程序員腦子里應該想的是如何整體操作一個表或者是幾個表。 但也有一些情況下,試圖整表操作是不現實的,需要一行一行處理數據,這種情況下,SQL語言提供了所謂游標的…

《假如編程是魔法之零基礎看得懂的Python入門教程 》——(一)既然你選擇了這系列教程那么我就要讓你聽得懂

一、前言 幾個月前編寫了一份python語言入門的博文&#xff0c;近期重新審閱了一遍發現編寫的質量太過隨意&#xff0c;可能對于一部分人并不是非常友好&#xff0c;故此重新編寫Python語言的零基礎教程。 本篇教程將會盡量把一些專業術語給讀者講解清楚&#xff0c;并且讓讀…

centos 7下安裝mysql_Centos7下安裝MySQL5.7(數據庫的最全安裝方法)

Centos7下使用yum安裝mysql數據庫首先Centox7已經不支持mysql&#xff0c;因為收費了你懂得&#xff0c;所以內部集成了mariadb&#xff0c;而安裝mysql的話會和mariadb的文件沖突&#xff0c;所以需要先卸載掉mariadb。由于確定使用mysql&#xff0c;那只有卸載mariadb了。一、…

環形隊列

在網上看到一篇比較好的介紹隊列的文章&#xff0c;地址為&#xff1a;http://www.cnblogs.com/kubixuesheng/p/4104802.html 特此感謝原創作者&#xff0c;以下均為摘抄。 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1、…

HTTP1.0、HTTP1.1 、SPDY、HTTP2.0之演變過程和優化

一、協議的演變過程和時間 HTTP1.0(1996年) -> HTTP1.1(1999年) -> SPDY(2012年google提出了SPDY的方案) -> HTTP2.0(2013年8月進行首次合作共事性測試) 二、影響一個HTTP網絡請求的因素 主要有兩個:帶寬和延遲 1)帶寬:網絡基礎建設已經使得帶寬得到極大的提升…

OK335xS GPMC nand device register hacking

/********************************************************************************** OK335xS GPMC nand device register hacking* 說明&#xff1a;* 由于最近遇到No NAND device found這個內核錯誤&#xff0c;在網絡上也沒找到很好的* 解決辦法&am…

Blazor University (19)使用 RenderFragments 模板化組件 —— 數據傳遞

原文鏈接&#xff1a;https://blazor-university.com/templating-components-with-renderfragements/passing-data-to-a-renderfragement/將數據傳遞給 RenderFragment源代碼[1]到目前為止&#xff0c;我們使用了僅包含子標記的 RenderFragments&#xff0c;然后在渲染組件時按…

一頭扎進Node(三) - File System

file.open:異步模式打開文件 fs.open(path, flags[, mode], callback) 案例代碼如下&#xff1a; var fs require(fs);/*** 參數說明&#xff1a;* 1.path&#xff1a;要打開的文件的文件路徑* 2.flags&#xff1a;打開文件的方式 讀/寫* r&#xff1a;只讀方式打開文件…

《零基礎看得懂的C語言入門教程 》——(十二)原來結構體是這么回事

一、學習目標 了解C語言的結構體的使用方法了解C語言結構體的結構的賦值了解多種C語言結構體變量的賦值方法和取值方法 目錄 C語言真的很難嗎&#xff1f;那是你沒看這張圖&#xff0c;化整為零輕松學習C語言。 第一篇&#xff1a;&#xff08;一&#xff09;脫離學習誤區 第…

【學生選課系統經典】C#與SQLSERVER連接:Windows應用工程案例

實驗任務描述 1 用C#訪問SQLSERVER數據庫(兩種安全模式); 2 用C#完成數據庫指定表上的數據顯示; 3 用C#完成數據庫指定表上的數據插入、刪除和更新; 4 用C#完成數據庫用戶驗證。 注意,由于C#語言的強大功能,下面的代碼適用于SQLSERVER2000、也適合于SQLSERVER2005。區別僅…