Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Třída MemoryOwner<T> je typ vyrovnávací paměti (buffer), který implementuje IMemoryOwner<T>. Zahrnuje vloženou vlastnost délky a řadu rozhraní API orientovaných na výkon. Je to v podstatě lehká obálka kolem ArrayPool<T> typu, s některými dalšími pomocnými užitečnými funkcemi.
Rozhraní API platformy:
MemoryOwner<T>,AllocationMode
Jak to funguje
MemoryOwner<T> má následující hlavní funkce:
- Jedním z hlavních problémů s poli vrácenými rozhraními
ArrayPool<T>API a instancemi vrácenými rozhranímiIMemoryOwner<T>API je, že zadaná velikost se používá pouze jakoMemoryPool<T>velikost: skutečná velikost vrácených vyrovnávacích pamětí může být větší.MemoryOwner<T>tento problém řeší také uložením původní požadované velikosti, takžeMemory<T>instanceSpan<T>načtené z ní nemusí být nikdy ručně řezovány. - Při použití
IMemoryOwner<T>nejprve získáte instanciSpan<T>, a teprve poté je nutné získatMemory<T>pro základní vyrovnávací paměť. Tento proces je poměrně nákladný a často zbytečný, protože možná nebudete potřebovat mezistupeňMemory<T>. Místo toho máMemoryOwner<T>dalšíSpanvlastnost, která je extrémně účinná, jelikož přímo obaluje interníT[]pole pronajaté z fondu. - Fond nevymaže vyrovnávací paměti, které si ve výchozím nastavení pronajímá. Pokud fond nevymazal vyrovnávací paměti, když je předtím vrátil, mohou obsahovat nesmyslná data. Za normálních okolností je nutné tyto půjčené vyrovnávací paměti vymazat ručně, což může být zdlouhavé, zejména pokud se provádí často.
MemoryOwner<T>nabízí flexibilnější přístup prostřednictvímAllocate(int, AllocationMode)rozhraní API. Tato metoda nejen přiděluje novou instanci přesně požadované velikosti, ale také umožňuje určit, který režim přidělení se má použít: buď stejný jakoArrayPool<T>, nebo takový, který automaticky vymaže pronajatou vyrovnávací paměť. - Někdy si můžete pronajmout vyrovnávací paměť s větší velikostí, než jakou skutečně potřebujete, a pak změnit její velikost. Za normálních okolností byste si museli pronajmout novou vyrovnávací paměť a zkopírovat oblast zájmu ze staré vyrovnávací paměti. Místo toho
MemoryOwner<T>poskytujeSlice(int, int)rozhraní API, které vrací novou instanci zabalující zadanou oblast zájmu. Tento přístup přeskočí zapůjčení nové vyrovnávací paměti a úplné kopírování položek.
Syntaxe
Tady je příklad, jak si pronajmout vyrovnávací paměť a načíst Memory<T> instanci:
// Be sure to include this using at the top of the file:
using Microsoft.Toolkit.HighPerformance.Buffers;
using (MemoryOwner<int> buffer = MemoryOwner<int>.Allocate(42))
{
// Both memory and span have exactly 42 items.
Memory<int> memory = buffer.Memory;
Span<int> span = buffer.Span;
// Writing to the span modifies the underlying buffer.
span[0] = 42;
}
V tomto příkladu použijete blok using k deklaraci vyrovnávací paměti MemoryOwner<T>. Tento přístup je zvlášť užitečný, protože základní pole se automaticky vrátí do fondu na konci bloku. Pokud nemáte přímou kontrolu nad životností MemoryOwner<T> instance, vyrovnávací paměť se vrátí do fondu, když objekt dokončí uvolňování paměti. V obou případech se půjčené vyrovnávací paměti vždy správně vrátí do sdíleného fondu.
Kdy byste měli použít tento typ?
Můžete použít MemoryOwner<T> jako typ vyrovnávací paměti pro obecné účely. Minimalizuje počet přidělení v průběhu času, protože interně znovu používá stejná pole ze sdíleného fondu. Běžným případem použití je nahrazení new T[] přidělení polí, zejména pokud provádíte opakované operace, které buď vyžadují dočasnou vyrovnávací paměť pro práci, nebo mají za následek vytvoření vyrovnávací paměti.
Předpokládejme, že máte datovou sadu skládající se z řady binárních souborů a potřebujete číst všechny tyto soubory a zpracovávat je nějakým způsobem. Pokud chcete kód správně oddělit, můžete napsat metodu, která jednoduše čte jeden binární soubor, který může vypadat takto:
public static byte[] GetBytesFromFile(string path)
{
using Stream stream = File.OpenRead(path);
byte[] buffer = new byte[(int)stream.Length];
stream.Read(buffer, 0, buffer.Length);
return buffer;
}
Všimněte si výrazu new byte[] . Pokud čtete velký počet souborů, přidělíte mnoho nových polí, což klade velký tlak na garbage collector. Tento kód můžete chtít refaktorovat pomocí vyrovnávacích pamětí půjčených z fondu, například:
public static (byte[] Buffer, int Length) GetBytesFromFile(string path)
{
using Stream stream = File.OpenRead(path);
byte[] buffer = ArrayPool<T>.Shared.Rent((int)stream.Length);
stream.Read(buffer, 0, (int)stream.Length);
return (buffer, (int)stream.Length);
}
Pomocí tohoto přístupu si pronajímáte vyrovnávací paměti z fondu, což znamená, že ve většině případů se vyhnete přidělování paměti. Protože pronájem vyrovnávacích pamětí není ve výchozím nastavení vymazán, můžete také ušetřit čas potřebný k jejich naplnění nulami, což vám poskytne další malé zlepšení výkonu. Načtení 1 000 souborů v předchozím příkladu přináší celkovou velikost přidělení přibližně od 1 MB dolů na pouhých 1 024 bajtů. Efektivně přidělíte a znovu použijete pouze jednu vyrovnávací paměť.
Předchozí kód má dva hlavní problémy:
-
ArrayPool<T>může vrátit vyrovnávací paměti, které mají větší velikost, než je požadovaná. Chcete-li tento problém vyřešit, musíte vrátit n-tici, která také udává skutečnou použitou velikost ve vaší pronajaté vyrovnávací paměti. - Jednoduše vrácením pole musíte být opatrní, abyste správně sledovali jeho životnost a vrátili ho do příslušného fondu. Tento problém můžete obejít tím, že místo toho použijete
MemoryPool<T>a vrátíte instanciIMemoryOwner<T>, ale stále máte problém s tím, že pronajaté vyrovnávací paměti mají větší velikost, než potřebujete. Kromě toho má určité režijní náklady při načítáníIMemoryOwner<T>ke zpracování, protože se jedná o rozhraní, a skutečnost, že vždy nejprve potřebujete získat instanciSpan<T>, a potomMemory<T>.
Pokud chcete vyřešit oba tyto problémy, můžete tento kód znovu refaktorovat pomocí MemoryOwner<T>:
public static MemoryOwner<byte> GetBytesFromFile(string path)
{
using Stream stream = File.OpenRead(path);
MemoryOwner<byte> buffer = MemoryOwner<byte>.Allocate((int)stream.Length);
stream.Read(buffer.Span);
return buffer;
}
Vrácená IMemoryOwner<byte> instance uvolní základní vyrovnávací paměť a vrátí ji do fondu při vyvolání její IDisposable.Dispose metody. Můžete ji použít k získání Memory<T> instance nebo Span<T> k interakci s načtenými daty a následnému odstranění instance, když ji už nepotřebujete. Kromě toho všechny MemoryOwner<T> vlastnosti (například MemoryOwner<T>.Span) respektují vámi požadovanou počáteční velikost, takže už nemusíte ručně sledovat efektivní velikost v pronajaté paměti.
Příklady
Další příklady najdete v jednotkových testech.
.NET Community Toolkit