最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

.NET使用Collections.Pooled提升性能優(yōu)化的方法

 更新時間:2022年05月25日 10:02:45   作者:InCerry  
這篇文章主要介紹了.NET使用Collections.Pooled性能優(yōu)化的方法,今天要給大家分享類庫Collections.Pooled,它是通過池化內(nèi)存來達(dá)到降低內(nèi)存占用和GC的目的,另外也會帶大家看看源碼,為什么它會帶來這些性能提升,一起通過本文學(xué)習(xí)下吧

簡介

性能優(yōu)化就是如何在保證處理相同數(shù)量的請求情況下占用更少的資源,而這個資源一般就是CPU或者內(nèi)存,當(dāng)然還有操作系統(tǒng)IO句柄、網(wǎng)絡(luò)流量、磁盤占用等等。但是絕大多數(shù)時候,我們就是在降低CPU和內(nèi)存的占用率。
之前分享的內(nèi)容都有一些局限性,很難直接改造,今天要和大家分享一個簡單的方法,只需要替換幾個集合類型,就可以達(dá)到提升性能和降低內(nèi)存占用的效果。
今天要給大家分享一個類庫,這個類庫叫Collections.Pooled,從名字就可以看出來,它是通過池化內(nèi)存來達(dá)到降低內(nèi)存占用和GC的目的,后面我們會直接來看看它的性能到底怎么樣,另外也會帶大家看看源碼,為什么它會帶來這些性能提升。

Collections.Pooled

項(xiàng)目鏈接:https://github.com/jtmueller/Collections.Pooled
該庫基于System.Collections.Generic中的類,這些類已經(jīng)被修改,以利用新的System.Span<T>System.Buffers.ArrayPool<T>類庫,達(dá)到減少內(nèi)存分配,提高性能,并允許與現(xiàn)代API的更大的互操作性的目的。
Collections.Pooled支持.NETStandard2.0(.NET Framework 4.6.1+),以及針對.NET Core 2.1+的優(yōu)化構(gòu)建。一套廣泛的單元測試和基準(zhǔn)已經(jīng)從corefx移植過來。

測試總數(shù):27501。通過:27501。失?。?。跳過:0。
測試運(yùn)行成功。
測試執(zhí)行時間:9.9019秒

如何使用

通過Nuget就可以很簡單的安裝這個類庫,NuGet Version 。

Install-Package Collections.Pooled
dotnet add package Collections.Pooled
paket add Collections.Pooled

Collections.Pooled類庫中,它針對我們常使用的集合類型都實(shí)現(xiàn)了池化的版本,和.NET原生類型的對比如下所示。

.NET原生Collections.Pooled備注
List<T>PooledList<T>泛型集合類
Dictionary<TKey, TValue>PooledDictionary<TKey, TValue>泛型字典類
HashSet<T>PooledSet<T>泛型哈希集合類
Stack<T>Stack<T>泛型棧
Queue<T>PooledQueue<T>泛型隊列

在使用時,我們只需要將對應(yīng)的.NET原生版本換成Collections.Pooled版本就可以了,如下方的代碼所示:

using Collections.Pooled;
// 使用方式是一樣的
var list = new List<int>();
var pooledList = new PooledList<int>();
var dictionary = new Dictionary<int,int>();
var pooledDictionary = new PooledDictionary<int,int>();
// 包括PooledSet、PooledQueue、PooledStack的使用方法都是一樣的
var pooledList1 = Enumerable.Range(0,100).ToPooledList();
var pooledDictionary1 = Enumerable.Range(0,100).ToPooledDictionary(i => i, i => i);

但是我們需要注意,Pooled類型實(shí)現(xiàn)了IDispose接口,它通過Dispose()方法將使用的內(nèi)存歸還到池中,所以我們需要在使用完Pooled集合對象以后調(diào)用它的Dispose()方法?;蛘呖梢灾苯邮褂?code>using var關(guān)鍵字。

using Collections.Pooled;
// 使用using var 會在pooled對象使用完畢后自動釋放
using var pooledList = new PooledList<int>();
Console.WriteLine(pooledList.Count);
// 使用using作用域 作用域結(jié)束以后就會釋放
using (var pooledDictionary = new PooledDictionary<int, int>())
{
	Console.WriteLine(pooledDictionary.Count);
}
// 手動調(diào)用Dispose方法
var pooledStack = new PooledStack<int>();
Console.WriteLine(pooledStack.Count);
pooledList.Dispose();

注意:使用Collections.Pooled內(nèi)的集合對象最好需要釋放掉它,不過不釋放也沒有關(guān)系,GC最終會回收它,只是它不能歸還到池中,達(dá)不到節(jié)省內(nèi)存的效果了。
由于它會復(fù)用內(nèi)存空間,在將內(nèi)存空間返回到池中的時候,需要對集合內(nèi)的元素做處理,它提供了一個叫ClearMode的枚舉供使用,定義如下:

namespace Collections.Pooled
{
    /// <summary>
    /// 這個枚舉允許控制在內(nèi)部數(shù)組返回到ArrayPool時如何處理數(shù)據(jù)。
    /// 數(shù)組返回到ArrayPool時如何處理數(shù)據(jù)。在使用默認(rèn)選項(xiàng)之外的其他選項(xiàng)之前,請注意了解 
    /// 在使用默認(rèn)值A(chǔ)uto之外的任何其他選項(xiàng)之前,請仔細(xì)了解每個選項(xiàng)的作用。
    /// </summary>
    public enum ClearMode
    {
        /// <summary>
        /// <para><code>Auto</code>根據(jù)目標(biāo)框架有不同的行為</para>
        /// <para>.NET Core 2.1: 引用類型和包含引用類型的值類型在內(nèi)部數(shù)組返回池時被清除。 不包含引用類型的值類型在返回池時不會被清除。</para>
        /// <para>.NET Standard 2.0: 在返回池之前清除所有用戶類型,以防它們包含引用類型。 對于 .NET Standard,Auto 和 Always 具有相同的行為。</para>
        /// </summary>
        Auto = 0,
        
        /// <summary>
        /// The <para><code>Always</code> 設(shè)置的效果是在返回池之前總是清除用戶類型。
        /// </summary>
        Always = 1,
        
        /// <summary>
        /// <para><code>Never</code> 將導(dǎo)致池化集合在將它們返回池之前永遠(yuǎn)不會清除用戶類型。</para>
        /// </summary>
        Never = 2
    }
}

默認(rèn)情況下,使用默認(rèn)值A(chǔ)uto即可,如果有特殊的性能要求,知曉風(fēng)險后可以使用Never。
對于引用類型和包含引用類型的值類型,我們必須在將內(nèi)存空間歸還到池的時候清空數(shù)組引用,如果不清除會導(dǎo)致GC無法釋放這部分內(nèi)存空間(因?yàn)樵氐囊靡恢北怀爻钟校?,如果是純值類型,那么就可以不清空,在使用結(jié)構(gòu)體替代類這篇文章中,我描述了引用類型和結(jié)構(gòu)體(值類型)數(shù)組的存儲區(qū)別,純值類型沒有對象頭回收也無需GC介入。

性能對比

我沒有單獨(dú)做Benchmark,直接使用的開源項(xiàng)目的跑分結(jié)果,很多項(xiàng)目的內(nèi)存占用都是0,那是因?yàn)槭褂玫某鼗膬?nèi)存,沒有多余的分配。

PooledList<T>

在Benchmark中循環(huán)向集合添加2048個元素,.NET原生的List<T>需要110us(根據(jù)實(shí)際跑分結(jié)果,圖中的毫秒應(yīng)該是筆誤)和263KB內(nèi)存,而PooledList<T>只需要36us和0KB內(nèi)存。

PooledDictionary<TKey, TValue>

在Benchmark中循環(huán)向字典添加10_0000個元素,.NET原生的Dictionary<TKey, TValue>需要11ms和13MB內(nèi)存,而PooledDictionary<TKey, TValue>只需要7ms和0MB內(nèi)存。

PooledSet<T>

在Benchmark中循環(huán)向哈希集合添加10_0000個元素,.NET原生的HashSet<T>需要5348ms和2MB,而PooledSet<T>只需要4723ms和0MB內(nèi)存。

PooledStack<T>

在Benchmark中循環(huán)向棧添加10_0000個元素,.NET原生的PooledStack<T>需要1079ms和2MB,而PooledStack<T>只需要633ms和0MB內(nèi)存。

PooledQueue<T>

在Benchmark中循環(huán)向隊列添加10_0000個元素,.NET原生的PooledQueue<T>需要681ms和1MB,而PooledQueue<T>只需要408ms和0MB內(nèi)存。

未手動釋放場景

另外在上文中我們提到了Pooled的集合類型需要釋放,但是不釋放也沒有太大的關(guān)系,因?yàn)镚C會去回收。

private static readonly string[] List = Enumerable  
    .Range(0, 10000).Select(c => c.ToString()).ToArray();  
// 使用默認(rèn)的集合類型  
[Benchmark(Baseline = true)]  
public int UseList()  
{  
    var list = new List<string>(1024);  
    for (var index = 0; index < List.Length; index++)  
    {  
        var item = List[index];  
        list.Add(item);  
    }  
    return list.Count;  
}  
// 使用PooledList 并且及時釋放  
[Benchmark]  
public int UsePooled()  
{  
    using var list = new PooledList<string>(1024);  
    for (var index = 0; index < List.Length; index++)  
    {  
        var item = List[index];  
        list.Add(item);  
    }  
    return list.Count;  
}  
// 使用PooledList 不釋放  
[Benchmark]  
public int UsePooledWithOutUsing()  
{  
    var list = new PooledList<string>(1024);  
    for (var index = 0; index < List.Length; index++)  
    {  
        var item = List[index];  
        list.Add(item);  
    }  
    return list.Count;  
}

Benchmark結(jié)果如下:

可以從上面的Benchmark結(jié)果可以得出結(jié)論。

  • 及時釋放Pooled類型集合幾乎不會觸發(fā)GC和分配內(nèi)存,從上圖中它只分配了56Byte內(nèi)存。
  • 就算不釋放Pooled類型集合,因?yàn)樗鼜某刂蟹峙鋬?nèi)存,在進(jìn)行ReSize擴(kuò)容操作時還是會復(fù)用內(nèi)存,另外跳過了GC分配內(nèi)存初始化步驟,速度也比較快。
  • 最慢的就是使用普通集合類型,每次ReSize擴(kuò)容操作都需要申請新的內(nèi)存空間,GC也要回收之前的內(nèi)存空間。

原理解析

如果大家看過我之前的博文你應(yīng)該為集合類型設(shè)置初始大小和淺析C# Dictionary實(shí)現(xiàn)原理就可以知道,.NET BCL開發(fā)人員為了高性能的隨機(jī)訪問,這些基本集合類型的底層數(shù)據(jù)結(jié)構(gòu)都是數(shù)組,我們以List<T>為例。

  • 創(chuàng)建新的數(shù)組來存儲添加進(jìn)來的元素。
  • 如果數(shù)組空間不夠,那么就觸發(fā)擴(kuò)容操作,申請2倍的空間大小。

構(gòu)造函數(shù)代碼如下,可以看到是直接創(chuàng)建的泛型數(shù)組:

public List(int capacity)
{
      if (capacity < 0)
          ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);

      if (capacity == 0)
          _items = s_emptyArray;
      else
          _items = new T[capacity];
}

那么如果想要池化內(nèi)存,只需要把類庫中使用new關(guān)鍵字申請的地方,改為使用池化的申請。這里和大家分享.NET BCL中的一個類型,叫ArrayPool,它提供了可重復(fù)使用的泛型實(shí)例的數(shù)組資源池,使用它可以降低對GC的壓力,在頻繁創(chuàng)建和銷毀數(shù)組的情況下提升性能。
而我們Pooled類型的底層就是使用ArrayPool來共享資源池,從它的構(gòu)造函數(shù)中,我們可以看到它默認(rèn)使用的是ArrayPool<T>.Shared來分配數(shù)組對象,當(dāng)然你也可以創(chuàng)建自己的ArrayPool來讓它使用。

// 默認(rèn)使用ArrayPool<T>.Shared池
public PooledList(int capacity, ClearMode clearMode, bool sizeToCapacity) : this(capacity, clearMode, ArrayPool<T>.Shared, sizeToCapacity) { }  

// 分配數(shù)組使用 ArrayPool
public PooledList(int capacity, ClearMode clearMode, ArrayPool<T> customPool, bool sizeToCapacity)
{
    if (capacity < 0)
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
    _pool = customPool ?? ArrayPool<T>.Shared;
    _clearOnFree = ShouldClear(clearMode);
    if (capacity == 0)
    {
        _items = s_emptyArray;
    }
    else
    {
        _items = _pool.Rent(capacity);
    }
    
    if (sizeToCapacity)
    {
        _size = capacity;
        if (clearMode != ClearMode.Never)
        {
            Array.Clear(_items, 0, _size);
        }
    }
 }

另外在進(jìn)行容量調(diào)整操作(擴(kuò)容)時,會將舊的數(shù)組歸還回線程池,新的數(shù)組也在池中獲取。

public int Capacity
{
    get => _items.Length;
    set
    {
        if (value < _size)
        {
            ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
        }

        if (value != _items.Length)
        {
            if (value > 0)
            {
                // 從池中分配數(shù)組
                var newItems = _pool.Rent(value);
                if (_size > 0)
                {
                    Array.Copy(_items, newItems, _size);
                }
                // 舊數(shù)組歸還到池中
                ReturnArray();
                _items = newItems;
            }
            else
            {
                ReturnArray();
                _size = 0;
            }
        }
    }
}
private void ReturnArray()  
{  
    if (_items.Length == 0)  
        return;  
    try  
    {  
        // 歸還到池中
        _pool.Return(_items, clearArray: _clearOnFree);  
    }  
    catch (ArgumentException)  
    {  
        // ArrayPool可能會拋出異常,我們直接吞掉 
    }  
    _items = s_emptyArray;  
}

另外作者使用了Span優(yōu)化了Add、Insert等等API,讓它們有更好的隨機(jī)訪問性能;另外還加入了TryXXX系列API,可以更方便的方式的使用它。比如List<T>類相比PooledList<T>就有多達(dá)170個修改。

總結(jié)

在我們線上實(shí)際的使用過程中,完全可以用Pooled提供的集合類型替代原生的集合類型,對降低內(nèi)存占用率和P95延時有非常大的幫助。
另外就算忘記釋放了,那性能也不會比使用原生的集合類型差多少。當(dāng)然最好的習(xí)慣就是及時的釋放它。

到此這篇關(guān)于.NET使用Collections.Pooled性能優(yōu)化的方法的文章就介紹到這了,更多相關(guān).net性能優(yōu)化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • .net MVC中使用forms驗(yàn)證詳解

    .net MVC中使用forms驗(yàn)證詳解

    這篇文章主要為大家詳細(xì)介紹了.net MVC中使用forms驗(yàn)證的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • ASP.NET MVC異步獲取和刷新ExtJS6 TreeStore

    ASP.NET MVC異步獲取和刷新ExtJS6 TreeStore

    這篇文章主要為大家詳細(xì)介紹了ASP.NET MVC異步獲取和刷新ExtJS6 TreeStore的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • ASP.NET 獲取存儲過程返回值的實(shí)現(xiàn)代碼

    ASP.NET 獲取存儲過程返回值的實(shí)現(xiàn)代碼

    ASP.NET 獲取存儲過程返回值的實(shí)現(xiàn)代碼,需要的朋友可以參考下。
    2011-12-12
  • SqlDataReader指定轉(zhuǎn)換無效的解決方法

    SqlDataReader指定轉(zhuǎn)換無效的解決方法

    這篇文章主要為大家詳細(xì)介紹了SqlDataReader指定轉(zhuǎn)換無效的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • ASP.NET+Web服務(wù)實(shí)現(xiàn)軟件共享

    ASP.NET+Web服務(wù)實(shí)現(xiàn)軟件共享

    ASP.NET+Web服務(wù)實(shí)現(xiàn)軟件共享...
    2006-09-09
  • ASP.NET實(shí)現(xiàn)個人信息注冊頁面并跳轉(zhuǎn)顯示

    ASP.NET實(shí)現(xiàn)個人信息注冊頁面并跳轉(zhuǎn)顯示

    這篇文章主要介紹了ASP.NET實(shí)現(xiàn)個人信息注冊頁面并跳轉(zhuǎn)顯示的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-12-12
  • ASP.NET用戶控件如何使用

    ASP.NET用戶控件如何使用

    這篇文章主要介紹了ASP.NET用戶控件的使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2015-09-09
  • Attribute/特性心得隨筆

    Attribute/特性心得隨筆

    從事asp.net工作的相關(guān)人員對Attribute并不陌生吧,本文就來為大家介紹下Attribute特性,下面有個不錯的示例,喜歡的朋友感受下
    2013-11-11
  • ASP.NET中AJAX的異步加載(Demo演示)

    ASP.NET中AJAX的異步加載(Demo演示)

    這篇文章主要介紹了ASP.NET中AJAX的異步加載(Demo演示),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • .NET Core3.0 日志 logging的實(shí)現(xiàn)

    .NET Core3.0 日志 logging的實(shí)現(xiàn)

    這篇文章主要介紹了.NET Core3.0 日志 logging的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10

最新評論

淮滨县| 游戏| 平乡县| 宁明县| 涡阳县| 宜宾县| 增城市| 包头市| 鞍山市| 朔州市| 宝应县| 皋兰县| 绥中县| 盘锦市| 盐城市| 资溪县| 资源县| 临洮县| 当雄县| 新兴县| 清原| 巴林左旗| 阳原县| 二手房| 尼玛县| 福建省| 普兰县| 克什克腾旗| 沐川县| 常山县| 天气| 龙海市| 阿城市| 海南省| 甘孜| 耿马| 平南县| 赞皇县| 炎陵县| 上林县| 贡觉县|