.net開發者應掌握的利器CommunityToolkit.HighPerformance——MemoryOwner與SpanOwner

MemoryOwner和SpanOwner都可以理解爲是對ArrayPool<>的一個包裝,無非一個是在堆棧上,一個是在託管堆上。既然做了包裝,那肯定隨之而來就是改進和優化。

MemoryOwner<>

MemoryOwner<>解決的問題

1.通過ArrayPool的Api MemoryPool.Shared.Rent(size) 獲得的IMemoryPool實例的緩衝區會大於我們指定的大小,一般是2的n次方,所以需要我們去切片去獲取真正有意義的段數據。但是MemoryOwner<>會存儲我們請求的大小,並且在其屬性中,如span,自動爲我們切片成我們請求的大小,從而免除了切片的操作。

2.IMemoryPool只有Memory屬性,我們需要通過Memory再去獲取span,但是MemoryOwner<>本身就有span屬性供我們調用

3.ArrayPool返回我們租用的緩衝空間的時候默認不會清空,除非設置clearArray,MemoryOwner<>在Allocate的時候就可以設置AllocationMode從而決定在返還緩衝區的時候是否清空其中數據。
但是清空數據將會帶來小小的性能消耗,就是我們需要給每一位填上默認值,即填充0

4.MemoryOwner<>最大的優點,就是會重複使用緩衝池中的某個相同的數組,從而最大程度的避免分配。

如下是我們使用原始數組來存儲從文件讀取到的數據:

using Stream stream = File.OpenRead(path);
byte[] buffer = new byte[(int)stream.Length];
stream.Read(buffer, 0, buffer.Length);
return buffer;

如果我們讀取的是一個大文件,則會在內存中分配一個大內存空間,這會在使用完之後給GC很大的壓力。

我們使用ArrayPool對代碼做一個優化,目的在於從緩衝池中租用一段空間,以避免空間的分配。

using Stream stream = File.OpenRead(path);
byte[] buffer = ArrayPool<byte>.Shared.Rent((int)stream.Length);
stream.Read(buffer, 0, (int)stream.Length);
//切片數組
return buffer[0..((int)stream.Length - 1)];

上述代碼有一個很明顯的問題,就是最終我們對數組做了切片,所以還是將舊的緩衝區的數據拷貝到了一個新的數組,並且還存在了分配空間的行爲。問題的根源就是ArrayPool租用到的大小實際會大於我們的實際請求。
並且我們返回了數組,那麼我們還需要再去跟蹤這個數組的使用的生命週期,並且需要再合適的時機去調用ArrayPool<>.Shared.Return(buffer)返還到緩衝池中。

爲了解決上述問題,我們再使用MemoryOwner<>去重構代碼

using Stream stream = File.OpenRead(path);
MemoryOwner<byte> buffer = MemoryOwner<byte>.Allocate((int)stream.Length);
stream.Read(buffer.Span);
return buffer;

MemoryOwner<>.Allocate(size)返回的IMemoryOwner<> 實例將負責釋放基礎緩衝區
並且MemoryOwner中的所有屬性遵循我們請求的實際大小,從而無需再做切片處理,比如Span屬性。

SpanOwner<>

SpanOwner<>是從共享內存池租用再堆棧中的緩衝區的類型,功能和API與MemoryOwner<>類似。
和MemoryOwner<>的區別就是它在堆棧上以及它沒有實現IMemoryOwner<>這個接口所以沒有Memory<>屬性。
代碼示例:

SpanOwner<int> buffer = SpanOwner<int>.Allocate(length);
Span<int> span = buffer.Span;

總結

1.SpanOwner和MemoryOwner常被用作緩衝區,存儲臨時數據
2.可以使用using對SpanOwner和MemoryOwner進行生命週期的控制,對於MemoryOwner來說,如果不方便控制,GC最後也會將其返還到緩衝池中。
3.SpanOwner和MemoryOwner可以理解爲是對ArrayPool的包裝。

本文參考文檔:https://learn.microsoft.com/zh-cn/dotnet/communitytoolkit/high-performance/memoryowner

如有問題,多謝指教!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章