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> sınıfı, IMemoryOwner<T> uygulayan bir arabellek türüdür. Bir katıştırılmış uzunluk özelliği ve bir dizi performans odaklı API içerir. Temelde, ArrayPool<T> türü etrafında hafif bir sarmalayıcıdır ve bazı ek yardımcı araçlar içerir.
Platform API'leri:
MemoryOwner<T>,AllocationMode
Nasıl çalışır?
MemoryOwner<T> aşağıdaki ana özelliklere sahiptir:
-
ArrayPool<T>API'ler tarafından döndürülen diziler veIMemoryOwner<T>API'ler tarafından döndürülenMemoryPool<T>örnekleriyle ilgili ana sorunlardan biri, belirttiğiniz boyutun yalnızca asgari boyut olarak kullanılmasıdır: döndürülen arabelleklerin gerçek boyutu daha büyük olabilir.MemoryOwner<T>bu sorunu çözmek için istenen özgün boyutu da depolar, bu nedenleMemory<T>veSpan<T>ondan alınan örneklerin asla el ile dilimlenmiş olması gerekmez. - Kullandığınızda
IMemoryOwner<T>arabellek için birSpan<T>almak istiyorsanız önce birMemory<T>örneğine ve sonra da birSpan<T>e ihtiyacınız vardır. AraMemory<T>öğesine ihtiyacınız olmadığından bu işlem oldukça pahalıdır ve genellikle gereksizdir. Bunun yerine,MemoryOwner<T>, havuzdan kiralanan içSpandiziyi doğrudan sarmaladığı için son derece hafif birT[]özelliğine sahiptir. - Havuz varsayılan olarak kiraladığı arabellekleri temizlemez. Havuz daha önce döndürdükleri arabellekleri temizlemediyse, bunlar çöp verileri içerebilir. Normalde, bu kiralanan arabellekleri el ile temizlemeniz gerekir, bu da özellikle sık yapıldığında zahmetli olabilir.
MemoryOwner<T>API aracılığıylaAllocate(int, AllocationMode)daha esnek bir yaklaşım sunar. Bu yöntem, yalnızca istenen boyutta yeni bir örnek ayırmakla kalmaz, aynı zamanda hangi ayırma modunun kullanılacağını belirtmenize de olanak tanır:ArrayPool<T>ile aynı olan mod veya kiralanan arabelleği otomatik olarak temizleyen mod. - Bazen ihtiyacınız olandan daha büyük bir arabellek kiralayıp sonra yeniden boyutlandırabilirsiniz. Normalde, yeni bir arabellek kiralamanız ve eski arabellekten ilgilendiğiniz bölgeyi kopyalamanız gerekir. Bunun yerine,
MemoryOwner<T>belirtilen ilgi alanını saran yeni bir örnek döndüren bir APISlice(int, int)sunar. Bu yaklaşım, yeni bir arabellek oluşturmaktan ve öğeleri tamamen kopyalamaktan kaçınır.
Sözdizimi
Arabelleği kiralama ve Memory<T> örneğini alma örneği aşağıda verilmiştir.
// 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, using arabelleğini tanımlamak için bir MemoryOwner<T> blok kullanırsınız. Bu yaklaşım özellikle yararlıdır çünkü temel dizi bloğun sonunda havuza otomatik olarak döndürülür. Doğrudan MemoryOwner<T> örneğinin ömrü üzerinde denetiminiz yoksa, çöp toplayıcı nesneyi sonlandırdığında arabellek havuza geri döndürülür. Her iki durumda da kiralanan arabellekler paylaşılan havuza her zaman doğru şekilde döndürülür.
Bu türü ne zaman kullanmalısınız?
MemoryOwner<T>'yi genel amaçlı bir arabellek türü olarak kullanabilirsiniz. Paylaşılan bir havuzdan aynı dizileri dahili olarak yeniden kullanacağından zaman içindeki ayırma sayısını en aza indirir. Yinelenen işlemler yaparken, çalışmak için geçici bir arabelleğe ihtiyaç duyduğunuzda veya sonuç olarak bir arabellek oluşturduğunuzda, özellikle new T[] dizi ayırmalarını değiştirmek yaygın bir kullanım örneğidir.
Bir dizi ikili dosyadan oluşan bir veri kümeniz olduğunu ve tüm bu dosyaları okumanız ve bunları bir şekilde işlemeniz gerektiğini varsayalım. Kodunuzu düzgün bir şekilde ayırmak için tek bir ikili dosyayı okuyan bir yöntem yazabilirsiniz. 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;
}
Dikkat edin new byte[] ifadesine. Çok sayıda dosya okursanız, çöp toplayıcı üzerinde aşırı yük oluşturan birçok yeni dizi ayırırsınız. Bir havuzdan kiralanan tampon bellekleri kullanarak bu kodu gözden geçirip yeniden yapılandırmak isteyebilirsiniz, ö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 havuzdaki arabellekleri kiralarsınız; bu da çoğu durumda ayırmayı atladığınız anlamına gelir. Ayrıca, kiralanan arabellekler varsayılan olarak temizlenmediğinden, bunları sıfırlarla doldurmak için gereken zamandan da tasarruf edebilirsiniz ve bu da size küçük bir performans artışı daha sağlar. Yukarıdaki örnekte, 1.000 dosya yüklemek yaklaşık 1 MB olan toplam ayırma boyutunu yalnızca 1.024 bayta düşürmektedir. Tek bir arabelleği etkili bir şekilde ayırıp yeniden kullanabilirsiniz.
Yukarıdaki kodda iki ana sorun vardır:
-
ArrayPool<T>istenen boyuttan daha büyük arabellekler döndürebilir. Bu sorunu çözmek için, kiraladığınız arabellekte kullanılan gerçek boyutu da gösteren bir demet döndürmeniz gerekir. - Yalnızca bir dizi döndürerek, ömrünü düzgün bir şekilde izlemek ve uygun havuza döndürmek için fazladan dikkatli olmanız gerekir. Bu sorunu, yerine
MemoryPool<T>kullanarak ve birIMemoryOwner<T>örneği döndürerek aşabilirsiniz, ancak yine de kiralanmış arabelleklerin, ihtiyacınız olandan daha büyük bir boyuta sahip olması sorunuyla karşılaşıyorsunuz. Ayrıca,IMemoryOwner<T>bir arabirim olduğundan ve her zaman önce birSpan<T>örneği, ardından birMemory<T>ve son olarak da üzerinde çalışılacak birSpan<T>almanız gerektiğinden, bazı ek yükleri vardır.
Bu iki sorunu da çözmek için kullanarak MemoryOwner<T>bu kodu yeniden düzenleyebilirsiniz:
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, IDisposable.Dispose yöntemi çağrıldığında temel arabelleği atar ve havuza geri döndürür. Yüklenen verilerle etkileşime geçmek için bir Memory<T> veya Span<T> örneği almak ve artık ihtiyacınız kalmadığında örneği atmak için kullanabilirsiniz. Buna ek olarak, tüm MemoryOwner<T> özellikler (gibi MemoryOwner<T>.Span) kullandığınız ilk istenen boyuta göredir, bu nedenle artık kiralanan arabellek içinde etkin boyutu el ile izlemeniz gerekmez.
Örnekler
.NET Community Toolkit