Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
MemoryOwner<T>, ekli uzunluk özelliği ve performans odaklı API'ler dizisi uygulayan IMemoryOwner<T>bir arabellek türüdür. Bu temelde bazı ek yardımcı yardımcı yardımcı programları ile türü etrafında ArrayPool<T> hafif bir sarmalayıcıdır.
Platform API'leri:
MemoryOwner<T>,AllocationMode
Nasıl çalışır?
MemoryOwner<T> aşağıdaki ana özelliklere sahiptir:
- API'ler tarafından
ArrayPool<T>döndürülen dizilerin ve API'ler tarafındanMemoryPool<T>döndürülen örneklerinIMemoryOwner<T>temel sorunlarından biri, kullanıcı tarafından belirtilen boyutun yalnızca en düşük boyut olarak kullanılmasıdır: döndürülen arabelleklerin gerçek boyutu aslında daha büyük olabilir.MemoryOwner<T>bu sorunu çözmek için istenen özgün boyutu depolar,Span<T>böyleceMemory<T>ve ondan alınan örneklerin asla el ile dilimlenmiş olması gerekmez. - kullanırken
IMemoryOwner<T>, temel alınan arabellek için birSpan<T>almak için önce bir örnek, sonra da birMemory<T>Span<T>gerekir. Bu oldukça pahalıdır ve genellikle gereksizdir, ara ara aslındaMemory<T>hiç gerekmeyebilir.MemoryOwner<T>bunun yerine, havuzdan kiralanan içT[]diziyi doğrudan sarmaladığı için son derece hafif olan ekSpanbir özelliği vardır. - Havuzdan kiralanan arabellekler varsayılan olarak temizlenmez, bu da havuza daha önce geri döndürülürken temizlenmediyse, çöp verileri içerebileceği anlamına gelir. Normalde, kullanıcıların bu kiralanan arabellekleri el ile temizlemeleri gerekir ve bu da özellikle sık yapıldığında ayrıntılı olabilir.
MemoryOwner<T>API aracılığıyla bu konuda daha esnek bir yaklaşımaAllocate(int, AllocationMode)sahiptir. Bu yöntem yalnızca tam olarak istenen boyutun yeni bir örneğini ayırmakla kalmaz, aynı zamanda hangi ayırma modunun kullanılacağını belirtmek için de kullanılabilir: ile aynıArrayPool<T>olan veya kiralanan arabelleği otomatik olarak temizleyen bir örnek. - Arabelleklerin gerçekten gerekenden daha büyük bir boyutta kiralanıp daha sonra yeniden boyutlandırılma durumları vardır. Bu normalde kullanıcıların yeni bir arabellek kiralamasını ve ilgilendiği bölgeyi eski arabellekten kopyalamasını gerektirir. Bunun yerine,
MemoryOwner<T>yalnızca belirtilen ilgi alanını sarmalayan yeni bir örnek döndüren bir API'yi kullanıma sunarSlice(int, int). Bu, yeni bir arabellek kiralamayı ve öğeleri tamamen kopyalamayı atlamayı sağlar.
Sözdizimi
Arabellek kiralama ve örnek alma işlemine bir örnek aşağıda verilmiştir Memory<T> :
// 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;
}
Bu örnekte, arabelleği bildirmek MemoryOwner<T> için bir using blok kullandık: Temel dizi bloğun sonundaki havuza otomatik olarak döndürüleceği için bu özellikle yararlıdır. Bunun yerine bir MemoryOwner<T> örneğin ömrü üzerinde doğrudan denetimimiz yoksa, nesne çöp toplayıcı tarafından sonlandırıldığında arabellek havuza döndürülür. Her iki durumda da, kiralanan arabellekler her zaman paylaşılan havuza doğru şekilde döndürülür.
Bu ne zaman kullanılmalıdır?
MemoryOwner<T> , paylaşılan bir havuzdan aynı dizileri dahili olarak yeniden kullandığından, zaman içinde yapılan ayırma sayısını en aza indirme avantajına sahip genel amaçlı bir arabellek türü olarak kullanılabilir. Yaygın bir kullanım örneği, özellikle üzerinde çalışmak için geçici bir arabelleğe ihtiyaç duyan veya sonuç olarak arabellek oluşturan yinelenen işlemler yaparken dizi ayırmalarını değiştirmektir new T[] .
Bir dizi ikili dosyadan oluşan bir veri kümemiz olduğunu ve tüm bu dosyaları okumamız ve bunları bir şekilde işlememiz gerektiğini varsayalım. Kodumuzu düzgün bir şekilde ayırmak için tek bir ikili dosyayı okuyan bir yöntem yazabiliriz. Bu yöntem şöyle görünebilir:
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;
}
Bu ifadeye new byte[] dikkat edin. Çok sayıda dosya okursak, çok sayıda yeni dizi ayıracağız ve bu da çöp toplayıcı üzerinde büyük baskı oluşturacak. Bu kodu bir havuzdan kiralanan arabellekleri kullanarak yeniden düzenlemek isteyebiliriz, örneğin:
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);
}
Bu yaklaşımı kullanarak arabellekler artık bir havuzdan kiralanır ve bu da çoğu durumda ayırmayı atlayabildiğimiz anlamına gelir. Ayrıca, kiralanan arabellekler varsayılan olarak temizlenmediğinden, bunları sıfırlarla doldurmak için gereken zamandan da tasarruf edebiliriz, bu da bize küçük bir performans iyileştirmesi daha sağlar. Yukarıdaki örnekte, 1000 dosya yüklenirse toplam ayırma boyutu yaklaşık 1 MB'tan yalnızca 1024 bayta indirilebilir. Yalnızca tek bir arabellek etkili bir şekilde ayrılır ve otomatik olarak yeniden kullanılır.
Yukarıdaki kodla ilgili iki ana sorun vardır:
ArrayPool<T>, istenenden daha büyük bir boyuta sahip arabellekler döndürebilir. Bu sorunu geçici olarak çözmek için, kiralanan arabellekte kullanılan gerçek boyutu da gösteren bir tanımlama grubu döndürmemiz gerekir.- Yalnızca bir dizi döndürerek, ömrünü düzgün bir şekilde izlemek ve uygun havuza döndürmek için çok dikkatli olmamız gerekir. Bunun yerine kullanarak
MemoryPool<T>ve bir örneği döndürerek bu sorunu geçici olarakIMemoryOwner<T>çözebiliriz, ancak yine de kiralanmış arabelleklerin ihtiyacımız olandan daha büyük bir boyuta sahip olması sorunuyla karşılaşıyoruz. Ayrıca,IMemoryOwner<T>bir arabirim olması ve her zaman önce bir örnek, sonraSpan<T>da birMemory<T>almamız gerektiği için üzerinde çalışılması gereken bir alınırkenSpan<T>bazı ek yükleri vardır.
Bu iki sorunu da çözmek için kullanarak MemoryOwner<T>bu kodu yeniden düzenleyebiliriz:
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;
}
Döndürülen IMemoryOwner<byte> örnek, yöntemi çağrıldığında IDisposable.Dispose temel alınan arabelleği yok etme ve havuza döndürme ile ilgilenir. Yüklenen verilerle etkileşime geçmek için bir Memory<T> veya Span<T> örneği almak ve artık ihtiyaç duymadığımızda örneği atmak için kullanabiliriz. Buna ek olarak, tüm MemoryOwner<T> özellikler (gibi MemoryOwner<T>.Span) kullandığımız ilk istenen boyuta göredir, bu nedenle artık kiralanan arabellek içindeki etkin boyutu el ile izlememiz gerekmez.
Örnekler
.NET Community Toolkit