• 《ASP.NET Core 高性能系列》Span<T>和Memory<T>

    一、Span<T>概述

      原文:Provides a type- and memory-safe representation of a contiguous region of arbitrary memory.

    中文的翻譯不準確,這里給出比較厚道的翻譯:提供類型T安全、連續的內存區域的表達方式.

       
    (圖1:Span<T>定義,不是全圖)

     

       這里出現高階語法 readonly ref struct,下面是msdn給的語言規范(或者其核心意義),估計大家會看暈, 

         Span<T> 并且不能跨 await 和 yield 邊界使用。 此外,對兩個方法的調用(Equals(Object) 和 GetHashCode)將引發一個 NotSupportedException。因為鎖定在堆棧上,所以也不要試圖讓其成為做為靜態成員。

     

    我先給出最簡單的解釋:

      Span<T>是微軟為了給.NET提供了一個高效的內存操縱元素,而定義的一個數據結構,為了高效的初衷,將Span<T>自身鎖定在堆棧上(內存連續,且處理高效)

    注意:是Span<T>自身!!!Span<T> 實例通常用于保存數組或某個數組的一部分的元素。

     二、Span<T>可用來做哪些事

      2.1 不得不提的 Slice

      切片這種東西,在GO,Rust中太尋常了(PS:當然對于C++的表示不屑),對于C#而言,這是一個性能提升不可或缺的概念,

    Span基本上就是這個概念的翻版.所以其中有諸多方法就是切片.

      

     

     

     

     

     可見微軟為Span提供了諸多類似于原來的String中的很多方法,具體查閱地址: Span的擴展方法

     

     2.1 切片是其本質,是對原有對象的投影(或部分投影)

      之前我們要實現高效的操作,如字符串類的操作,數組類的操作,

      這里應該尤其注意,不見得你使用Span就高效了,明白它的設計初衷:Slice! 特別是會不斷產生新的碎片和構造新對象的場景.(由此可見對于String的操作產生了諸多

    新碎片這樣的場景是尤其好用的)

      我們看看如下的場景,大家覺得哪個效率會更高

                int[] array = new int[10000];
                Span<int> arraySpan = array;
    
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                for (int ctr = 0; ctr < arraySpan.Length; ctr++)
                    arraySpan[ctr] = arraySpan[ctr] * arraySpan[ctr]/3;
                stopwatch.Stop();
                Console.WriteLine(stopwatch.Elapsed);
    
                array = new int[10000];
                stopwatch.Reset();
                stopwatch.Start();
                for (int ctr = 0; ctr < array.Length; ctr++)
                    array[ctr] = array[ctr] * array[ctr]/3;
                Console.WriteLine(stopwatch.Elapsed);        

      結果按照我們的原則你就知道,不用Span效果會更好,下圖是realse模式下發布的,整體上可知:不用Span會更快,所以切片是它的本質!大家看看GO的切片就知道了.

      

     

     3.1 Span<T>可以不僅投影常見對象還可以是從Marshal , stackalloc分配的而來的

     

    var native = Marshal.AllocHGlobal(100);
    Span<byte> nativeSpan;
    unsafe
    {
        nativeSpan = new Span<byte>(native.ToPointer(), 100);
    }
    byte data = 0;
    for (int ctr = 0; ctr < nativeSpan.Length; ctr++)
        nativeSpan[ctr] = data++;
    
    int nativeSum = 0;
    foreach (var value in nativeSpan)
        nativeSum += value;
    
    Console.WriteLine($"The sum is {nativeSum}");
    Marshal.FreeHGlobal(native);

     

    byte data = 0;
    Span<byte> stackSpan = stackalloc byte[100];
    for (int ctr = 0; ctr < stackSpan.Length; ctr++)
        stackSpan[ctr] = data++;
    
    int stackSum = 0;
    foreach (var value in stackSpan)
        stackSum += value;
    
    Console.WriteLine($"The sum is {stackSum}");
    

      

    三、Memory<T>概述

      和Span<T>類似,它同樣表示連續內存區域。區別是沒有Span<T>堆棧上的限制,沒有 readonly ref struct 這樣的申明了.

    這意味著 Memory<T> 可以放置在托管堆上,而 Span<T> 不能。 因此,Memory<T> 結構與 Span<T> 實例沒有相同的限制。

    具體而言:它可用作類中的字段。它可跨 await 和 yield 邊界使用。除了 Memory<T>之外,還可以使用 System.ReadOnlyMemory<T> 來表示不可變或只讀內存。

     

     

      這里有園友從C++源碼的角度進行分析,這里提取下面兩段,供大家參閱(鏈接地址),

    Span 與 Memory 的區別:

      1.Memory<T> 保存 原有的對象地址、子內容的開始地址 與 子內容的長度,大致情況下圖:

      

     

      如上文所說,Span被微軟鎖定在堆棧上,

      2.Span 保存子內容的開始地址與長度,不保存原始對象的地址,大致如下圖:

      

     

     

    如果這就是真實情況,可見Span脫離不了堆棧的環境的,不然計算不了真實的切片地址的.

     

    三、使用上的預測和建議

      1.類似于string這樣的操作會Span大有用處(因為會產生很多新的中間數據產生開銷)

      2.無論span還是memory設計初衷就是Slice,使用場景是那些會不斷產生新的開銷、新的對象的場景.

      3.其實所有的性能提升根本讓CPU運行的指令更少了,減少了不必要的開銷

     

     

    posted @ 2020-03-01 10:00  【秦時明月】  Views(...)  Comments(...Edit  收藏
    贵州快三平台贵州快三主页贵州快三网站贵州快三官网贵州快三娱乐贵州快三开户贵州快三注册贵州快三是真的吗贵州快三登入贵州快三快三贵州快三时时彩贵州快三手机app下载贵州快三开奖 镇巴县 | 湖州市 | 开阳县 | 隆昌县 | 宁乡县 | 潞城市 | 林芝县 | 义马市 | 双辽市 | 清涧县 | 内黄县 | 且末县 | 寿光市 | 阿尔山市 | 荆门市 | 奎屯市 | 左云县 | 泗洪县 | 长葛市 | 宁明县 | 曲松县 | 乌拉特中旗 | 祁门县 | 焦作市 | 阳新县 | 泌阳县 | 宣恩县 | 南安市 | 无为县 | 海伦市 | 兴隆县 | 鄯善县 | 建始县 | 佛学 | 抚州市 | 哈巴河县 | 桑植县 | 秀山 | 长宁区 | 津市市 | 阜康市 | 呼伦贝尔市 | 信阳市 | 渝北区 | 东乡 | 永清县 | 鸡西市 | 北宁市 | 梅河口市 | 苏尼特左旗 | 洪泽县 | 奉贤区 | 双辽市 | 客服 | 平远县 | 铅山县 | 库伦旗 | 黄大仙区 | 红安县 | 阜新市 | 江永县 | 南汇区 | 曲水县 | 青田县 | 富顺县 | 太原市 | 上栗县 | 博白县 | 康乐县 | 吕梁市 | 吉安县 | 蕲春县 | 陆丰市 | 江北区 | 荔波县 | 蒲城县 | 遵义县 | 绥江县 | 水城县 | 菏泽市 | 桦南县 | 邢台县 | 东乡族自治县 | 阳朔县 | 崇明县 | 咸阳市 | 舞钢市 | 邢台市 | 禹州市 | 晋中市 | 南江县 | 长春市 | 天等县 | 清苑县 | 三门县 | 肇庆市 | 陕西省 | 微博 | 都兰县 | 乌兰县 | 和硕县 | 和静县 | 凤山市 | 游戏 | 香格里拉县 | 鹤庆县 | 博罗县 | 嵊泗县 | 房产 | 蓬莱市 | 望都县 | 英德市 | 巴彦淖尔市 | 甘德县 | 嘉善县 | 黔西县 | 乌拉特前旗 | 武鸣县 | 天津市 | 贵溪市 | 威信县 | 吴桥县 | 绥滨县 | 垦利县 | 钟祥市 | 县级市 | 中卫市 | 托克逊县 | 焉耆 | 潮州市 | 正阳县 | 温州市 | 行唐县 | 陕西省 | 北流市 | 南安市 | 乌审旗 | 永德县 | 庆元县 | 民乐县 | 沂南县 | 尼勒克县 | 汶上县 | 浪卡子县 | 扎赉特旗 | 郯城县 | 米易县 | 三河市 | 新沂市 | 德安县 | 耒阳市 | 徐闻县 | 禹州市 | 茶陵县 | 乐安县 | 慈溪市 | 大同县 | 嘉峪关市 | 师宗县 | 砚山县 | 恩平市 | 万安县 | 邯郸县 | 普格县 | 淮北市 | 托里县 | 龙口市 | 黄大仙区 | 胶南市 | 夏河县 | 富阳市 | 理塘县 | 行唐县 | 安达市 | 沙洋县 | 名山县 | 壶关县 | 鄱阳县 | 嘉祥县 | 新乡县 | 广饶县 | 政和县 | 哈尔滨市 | 大石桥市 | 榆社县 | 巍山 | 邛崃市 | 曲阜市 | 黄山市 | 外汇 | 汉寿县 | 徐汇区 | 当雄县 | 大安市 | 青铜峡市 | 广东省 | 阿瓦提县 | 濮阳县 | 区。 | 车致 | 阿荣旗 | 伊川县 | 文昌市 | 巴南区 | 贺州市 | 驻马店市 | 闵行区 |