<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)用實(shí)踐

          共 5059字,需瀏覽 11分鐘

           ·

          2020-11-25 14:54

          一:背景

          1. 講故事

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

          • String 對(duì) Span / ReadOnlySpan 的支持

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

          • StringBuilder 對(duì) 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 對(duì) 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ǔ)的類都在大力對(duì)接 Span / ReadOnlySpan,更別說復(fù)雜類型了,其地位不言自明哈,接下來我們就從 Span 本身的機(jī)制聊起。

          二:Span 原理探究

          1. Span 源碼分析

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


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

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

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

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

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

          雖然圖已經(jīng)畫了,但還是有很多朋友希望眼見為實(shí),必須實(shí)操演練,嘿嘿,無懼任何挑戰(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,按照?qǐng)D的理論:0x000001e956b8ab20 應(yīng)該是 nums 數(shù)組元素 1 的內(nèi)存地址,可以用 dp 驗(yàn)證一下。


          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 依次排列,有些朋友可能有點(diǎn)小疑問,為啥 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個(gè)元素,不信的話我截一張圖:

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

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

          三:Span 在 String 和 List 的實(shí)踐

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

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

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

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

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


          ????????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é)果是很輕松的算出來了,但你仔細(xì)想想這里是不是有點(diǎn)什么問題,比如說為了從 word 中扣出 num,我用了兩次 SubString,就意味著會(huì)在 托管堆 上生成兩個(gè) string,如果說我執(zhí)行 1w 次話,那托管堆上會(huì)不會(huì)有 2w 個(gè) 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 的個(gè)數(shù)


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

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

          2) ?新式 Span 做法

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


          ????????????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;?
          ????????????}

          然后在 托管堆 驗(yàn)證一下,是不是沒有 臨時(shí) string 了?


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

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

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

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

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

          四:總結(jié)

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







          回復(fù)?【關(guān)閉】學(xué)關(guān)
          回復(fù)?【實(shí)戰(zhàn)】獲取20套實(shí)戰(zhàn)源碼
          回復(fù)?【被刪】學(xué)個(gè)
          回復(fù)?【訪客】學(xué)
          回復(fù)?【小程序】學(xué)獲取15套【入門+實(shí)戰(zhàn)+賺錢】小程序源碼
          回復(fù)?【python】學(xué)微獲取全套0基礎(chǔ)Python知識(shí)手冊(cè)
          回復(fù)?【2019】獲取2019 .NET 開發(fā)者峰會(huì)資料PPT
          回復(fù)?【加群】加入dotnet微信交流群

          終于GitHub App 已支持簡(jiǎn)體中文!


          微信錢包“免費(fèi)提現(xiàn)”的方法來了!




          瀏覽 35
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  欧美成人在线免费 | 亚洲最大视频网 | 91亚洲视频 | 激情A学生妹 | 骚逼网站视频 |