<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          終于,他還是對C# Span 下手了,源碼解讀和應(yīng)用實踐

          共 5156字,需瀏覽 11分鐘

           ·

          2020-11-17 05:18


          一:背景

          1. 講故事

          這兩天工作上太忙沒有及時持續(xù)的文章產(chǎn)出,和大家說聲抱歉,前幾天群里一個朋友在問什么時候可以產(chǎn)出 Span 的下一篇,哈哈,這就來啦!讀過上一篇的朋友應(yīng)該都知道 Span 統(tǒng)一了 .NET 程序?棧 + 托管 + 非托管?實現(xiàn)了三大塊內(nèi)存的統(tǒng)一訪問,??,而且在 .net 底層 Library 中也是一等公民的存在,很多現(xiàn)有的類都提供了對 Span / ReadOnlySpan 的支持。

          • String 對 Span / ReadOnlySpan 的支持

          ????public?sealed?class?String
          ????{
          ????????[MethodImpl(MethodImplOptions.InternalCall)]
          ????????[NullableContext(0)]
          ????????public?extern?String(ReadOnlySpan<char>?value);
          ????}

          • StringBuilder 對 Span / ReadOnlySpan 的支持

          ????public?sealed?class?StringBuilder?:?ISerializable
          ????{
          ????????public?unsafe?StringBuilder?Append(ReadOnlySpan<char>?value)
          ????????{
          ????????????if?(value.Length?>?0)
          ????????????{
          ????????????????fixed?(char*?value2?=?&MemoryMarshal.GetReference(value))
          ????????????????{
          ????????????????????Append(value2,?value.Length);
          ????????????????}
          ????????????}
          ????????????return?this;
          ????????}
          ????}

          • Int 對 Span / ReadOnlySpan 的支持

          ????public?readonly?struct?Int32
          ????{
          ????????public?static?int?Parse(ReadOnlySpan<char>?s,?NumberStyles?style?=?NumberStyles.Integer,?IFormatProvider??provider?=?null)
          ????????{
          ????????????NumberFormatInfo.ValidateParseStyleInteger(style);
          ????????????return?Number.ParseInt32(s,?style,?NumberFormatInfo.GetInstance(provider));
          ????????}
          ????}

          怎么樣,這些通用 & 基礎(chǔ)的類都在大力對接?Span / ReadOnlySpan,更別說復(fù)雜類型了,其地位不言自明哈,接下來我們就從 Span 本身的機制聊起。

          二:Span 原理探究

          1. Span 源碼分析

          靈活運用 Span 解決工作中的實際問題我相信大家應(yīng)該沒什么毛病了,有了這個基礎(chǔ)再從 Span 的源碼 和 用戶態(tài) 和大家一起深度剖析,從源碼開始吧。


          ????public?readonly?ref?struct?Span
          ????{
          ????????internal?readonly?ByReference?_pointer;

          ????????private?readonly?int?_length;
          ????}

          上面代碼的?ref struct?可以看出,這個 Span 是只可以分配在棧上的值類型,然后就是里面的 _pointer 和 _length 兩個實例字段,不知道看完這兩個字段腦子里是不是有一幅圖,大概是這樣的。

          可以清晰的看出,Span 就是用來映射一段可以連續(xù)訪問的內(nèi)存地址,空間大小由 length 控制,開始位置由 _pointer 指定,是不是像極了指針???,是的,語言團隊要保證你的程序高性能,還得照護你的人身安全,出了各種手段,真是煞費苦心!???

          2. Span 用戶態(tài)分析

          雖然圖已經(jīng)畫了,但還是有很多朋友希望眼見為實,必須實操演練,嘿嘿,無懼任何挑戰(zhàn),那我先把上面的圖化成代碼:


          ????????static?void?Main(string[]?args)
          ????????{
          ????????????var?nums?=?new?int[]?{?1,?2,?3,?4,?5,?6?};

          ????????????var?span =?new?Span<int>(nums);

          ????????????Console.ReadLine();
          ????????}

          接下來我用 windbg 把線程棧中的 span 也找出來。


          0:000>?!clrstack?-l
          OS?Thread?Id:?0x181c?(0)
          ????????Child?SP???????????????IP?Call?Site
          000000963277E5D0?00007ffc3e601434?ConsoleApp1.Program.Main(System.String[])?[E:\net5\ConsoleApp2\ConsoleApp1\Program.cs?@?13]
          ????LOCALS:
          ????????0x000000963277E618?=?0x000001e956b8ab10
          ????????0x000000963277E608?=?0x000001e956b8ab20

          從最后一行代碼可以看出:span 的棧地址是 0x000000963277E608,棧內(nèi)容是:0x000001e956b8ab20,按照圖的理論:0x000001e956b8ab20 應(yīng)該是 nums 數(shù)組元素 1 的內(nèi)存地址,可以用 dp 驗證一下。


          0:000>?dp?0x000001e956b8ab20
          000001e9`56b8ab20??00000002`00000001?00000004`00000003
          000001e9`56b8ab30??00000006`00000005?00000000`00000000
          000001e9`56b8ab40??00007ffc`3e6c4388?00000000`00000000

          從上面三行內(nèi)存地址來看,數(shù)組的:1,2,3,4,5,6?依次排列,有些朋友可能有點小疑問,為啥 nums 的內(nèi)存地址不是指向數(shù)組元素 1 的呢?那我來普及一下吧,先用 dp 喚出數(shù)組的內(nèi)存地址。


          0:000>?dp?0x000001e956b8ab10
          000001e9`56b8ab10??00007ffc`3e69f090?00000000`00000006
          000001e9`56b8ab20??00000002`00000001?00000004`00000003
          000001e9`56b8ab30??00000006`00000005?00000000`00000000

          可以看出,第一排為:?00007ffc3e69f090 0000000000000006, 前面的 8 byte 表示 數(shù)組 的 方法表地址,后面的 8byte 表示 6 ,也就是說數(shù)組有 6個元素,不信的話我截一張圖:

          span 是由 _pointer + length 組成的,剛才的 _pointer 也給大家演示了,那 length 的值在哪里呢?因為 span 是 struct,所以需要用 dp 把剛才的線程棧最小的棧地址打出來就可以了。

          到這里,我覺得我講的已經(jīng)夠清楚了,如果還有點懵的話可以仔細想一想哈。

          三:Span 在 String 和 List 的實踐

          Span的應(yīng)用場景真的是太多了,不可能在這篇一一列舉,這里我就舉兩個例子吧,讓大家能夠感受到 Span 的強大即可。

          1. 在 String 上的應(yīng)用

          案例:如何高效的計算出用戶輸入的值?10+20??

          1) ?傳統(tǒng) Substring 做法

          傳統(tǒng)的做法很簡單,截取唄,代碼如下:


          ????????static?void?Main(string[]?args)
          ????????{
          ????????????var?word?=?"10+20";

          ????????????var?splitIndex?=?word.IndexOf("+");

          ????????????var?num1?=?int.Parse(word.Substring(0,?splitIndex));

          ????????????var?num2?=?int.Parse(word.Substring(splitIndex?+?1));

          ????????????var?sum?=?num1?+?num2;

          ????????????Console.WriteLine($"{num1}+{num2}={sum}");

          ????????????Console.ReadLine();
          ????????}

          結(jié)果是很輕松的算出來了,但你仔細想想這里是不是有點什么問題,比如說為了從 word 中扣出 num,我用了兩次 SubString,就意味著會在 托管堆 上生成兩個 string,如果說我執(zhí)行 1w 次話,那托管堆上會不會有 2w 個 string 呢?修改代碼如下:


          ????????????for?(int?i?=?0;?i?10000;?i++)
          ????????????{
          ????????????????var?num1?=?int.Parse(word.Substring(0,?splitIndex));

          ????????????????var?num2?=?int.Parse(word.Substring(splitIndex?+?1));

          ????????????????var?sum?=?num1?+?num2;?
          ????????????}

          然后看一下 托管堆 上 String 的個數(shù)


          0:000>?!dumpheap?-type?String?-stat
          Statistics:
          ??????????????MT????Count????TotalSize?Class?Name
          00007ffc53a81e18????20167???????556538?System.String

          托管堆上有 20167 個,挺恐怖的,真的是給 GC 添麻煩哈,這里還有 167 個是系統(tǒng)自帶的,接下來的問題是有沒有辦法替換 SubString 從而不生成臨時string呢?

          2) ?新式 Span 做法

          如果看懂了 Span 結(jié)構(gòu)圖,你就應(yīng)該會使用 _pointer + length 將 string 進行切片處理,對不對,代碼如下:


          ????????????for?(int?i?=?0;?i?10000;?i++)
          ????????????{
          ????????????????var?num1?=?int.Parse(word.AsSpan(0,?splitIndex));

          ????????????????var?num2?=?int.Parse(word.AsSpan(splitIndex));

          ????????????????var?sum?=?num1?+?num2;?
          ????????????}

          然后在 托管堆 驗證一下,是不是沒有 臨時 string 了?


          0:000>?!dumpheap?-type?String?-stat
          Statistics:
          ??????????????MT????Count????TotalSize?Class?Name
          00007ffc53a51e18??????167????????36538?System.String

          可以看到就只有 167 個系統(tǒng)字符串,性能也得到了不小的提升,???。

          2. 在 List 上的應(yīng)用

          平時用 Span 的時候,更多的會應(yīng)用到 Array 上面,畢竟 Array 在托管堆上是連續(xù)內(nèi)存,方便 Span 在上面畫一個可視窗口,其實不僅僅是 Array,從 .NET5 ?開始在 List 上畫一個視圖也是可以的,截圖如下:

          因為 List 的 CURD 會導(dǎo)致底層的 Array 忽長忽短或重新分配,也就無法實現(xiàn)物理上的連續(xù)內(nèi)存,所以 Span 應(yīng)用到 List 之后,希望List是不可變的,這也是官方的建議。

          四:總結(jié)

          總的來說,Span 在 .NET 底層框架中的地位是越來越顯著了,相信 netCore 追求更高更快的性能上 Span 一定大有可為,大家趕緊學(xué)起來,???


          往期精彩回顧




          【推薦】.NET Core開發(fā)實戰(zhàn)視頻課程?★★★

          .NET Core實戰(zhàn)項目之CMS 第一章 入門篇-開篇及總體規(guī)劃

          【.NET Core微服務(wù)實戰(zhàn)-統(tǒng)一身份認證】開篇及目錄索引

          Redis基本使用及百億數(shù)據(jù)量中的使用技巧分享(附視頻地址及觀看指南)

          .NET Core中的一個接口多種實現(xiàn)的依賴注入與動態(tài)選擇看這篇就夠了

          10個小技巧助您寫出高性能的ASP.NET Core代碼

          用abp vNext快速開發(fā)Quartz.NET定時任務(wù)管理界面

          在ASP.NET Core中創(chuàng)建基于Quartz.NET托管服務(wù)輕松實現(xiàn)作業(yè)調(diào)度

          現(xiàn)身說法:實際業(yè)務(wù)出發(fā)分析百億數(shù)據(jù)量下的多表查詢優(yōu)化

          關(guān)于C#異步編程你應(yīng)該了解的幾點建議

          C#異步編程看這篇就夠了


          瀏覽 50
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  xjgggyxgs.com高价收liang,请涟系@qdd2000 | 开心成人激情 | 天天澡天天狠天干天 | 中国人与拘一级毛片 | AV乱伦片 |