.NET Core – Span ve Memory Türleri Nedir?

Merhaba,

Bu içeriğimizde, STACK yahut HEAP farketmeksizin bellekte ardışıl olarak tanımlanmış belirli bir bölgeye tip güvenliğiyle erişmemizi sağlayan Span<T> ve onun belirli kısıtlamalardan arındırılmış muadili olan Memory<T> struct türlerini inceliyor olacağız.

Nedir bu Span<T> Türü?

.NET Core - Span<T> ve Memory<T> Türleri Nedir?
Bellek üzerinde belirli bir alanı temsil ederek işlemler gerçekleştirmemizi sağlayan bir struct’tır. Bu belirli bir alandan kastımız tabi ki de bellekte ardışıl alan kaplayan array değerleridir. Biliyorsunuz ki en sade haliyle array’ler belleğin managed bölgesi olan HEAP’de tutulmakta, bunu STACK’te gerçekleştirebilmek için ise stackalloc keywordünden yararlanılmaktadır. Bu bilgiler ışığında Span, stack yahut heap farketmeksizin tanımlanmış olan array’in tümünü yahut bir bölümünü bizler için refere edebilen ve üzerinde işlemler gerçekleştirmemizi sağlayabilen kullanışlı bir yapıdır.

Evet, stack ve heap bölgelerinde tanımlanmış array’ler üzerinde tamamını yahut bir bölümünü refere ederek ortak iş yapabilmesi kullanışlılık açısından oldukça verimlilik, bu refere esnasında bellekte ekstradan alan tahsisinde bulunup lüzumsuz değer üretimine girmemesi performans ve maliyet açısından da yüksek kazanç getirmektedir.

Peki Span ile hangi sorunlara çözüm getirilmektedir?
Burada örnek olarak esasında direkt bir sorun kabul edilemeyecek string veri yapısını ele alabiliriz. string bir immutable(değişmez) tür olduğu için string bir ifade üzerinde yapılan tüm faaliyetler mevcut verinin kopyalanmasına ve böylece yeni bir değer üretimine sebep olacaktır. Dolayısıyla bu durum büyük değerlerde çok daha büyük maliyetlere sebep olmakla birlikte teselli olarak StringBuilder gibi nesnelerin kıymeti harbiyesini arttırmaktadır.

Span, string gibi maliyetli veriler üzerinde yapılacak operasyonlarda performans açısından yüksek getiriyle birlikte maliyeti olabildiğince düşürmekte ve ekstradan değer kopyalamaya gerek kalmaksızın tüm faaliyetleri gerçekleştirmemize olanak sağlamaktadır.

Peki Span nasıl oluşturulur?
Span tanımlayabilmek için aşağıdaki gibi çalışması

            int[] numbers = { 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25 };
            Span<int> span = numbers;

ya da aşağıdaki gibi Array türlerde olan AsSpan fonksiyonunun çağrılması

            int[] numbers = { 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25 };
            Span<int> span = numbers.AsSpan();

yeterlidir.

Tabi bu şekilde tanımlamalar ilgili dizinin tamamına karşılık gelmektedir. Eğer ki dizinin bir bölümüne karşılık gelen Span tanımlamak istiyorsanız;

            int[] numbers = { 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25 };
            Span<int> span = new Span<int>(array: numbers, start: 3, length: 5);

ya da

            int[] numbers = { 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25 };
            Span<int> span = numbers.AsSpan(start: 3, length: 5);

şeklinde çalışabilirsiniz.

Bu şekilde tanımlanan Span artık ilgili array referansı ile aynı nesneyi temsil etmekte ve üzerinden yapılan tüm işlemleri fiziksel olarak array’e işlemektedir.

            int[] numbers = { 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25 };
            Span<int> span = numbers;
            numbers.ToList().ForEach(n => Console.WriteLine(n));

            Console.WriteLine("****************");

            span[0] = 100;
            span[5] = 200;
            span[10] = 300;
            numbers.ToList().ForEach(n => Console.WriteLine(n));
3
5
7
9
11
13
15
17
19
21
23
25
****************
100
5
7
9
11
200
15
17
19
21
300
25

Elimizde bulunan herhangi bir Span değeri üzerinden yeni bir Span yaratabilmek için ise Slice metodu kullanılabilir.

            int[] numbers = { 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25 };
            Span<int> span = numbers;
            Span<int> span2 = span.Slice(3, 5);

ReadOnlySpan<T>

Span niteliklerinin tümünü sağlamakta lakin adı üzerinde sadece okunabilir kılmaktadır. Aşağıdaki gibi tanımlama yapıldığı taktirde kod derlenmeyecektir.

            int[] numbers = { 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25 };
            ReadOnlySpan<int> span = numbers;
            span[0] = 100;

ReadOnlySpan sayesinde bir dizideki yahut metindeki belirli bir alanı elde edebiliyor ve bir yandan da değiştirilmesine karşı orjinal veriyi güvenceye alabiliyorsunuz.

Stackalloc İle Span Kullanımı

İçeriğimizin başlarında stackalloc ile belleğin stack bölgesinde oluşturulan array’i Span ile temsil edebildiğimizden bahsettik. Bunun için normalde aşağıdaki gibi unsafe komutu ile oluşturulan dizi

            unsafe
            {
                int* p = stackalloc int[3];
                for (int i = 0; i < 3; i++)
                {
                    Console.WriteLine(p[i] = i * 5);
                }
            }

aşağıdaki gibi Span ile unsafe kullanmaksızın tanımlanabilmekte ve refere edilebilmektedir.

            Span<int> p = stackalloc int[3];
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine(p[i] = i * 5);
            }

Memory<T>

Span, ref struct olarak tasarlanmış bir struct’tır. Dolayısıyla heap’te allocation(tahsis) edilememektedir. Ayrıca object, dynamic veya interface türleri aracılığıyla referans edilememekte, await ile kullanılamamakta ve bir class içerisinde field yahut property olarak tanımlanamamaktadır. Span’ın bu kısıtlamalardan münezzeh muadili olan Memory ve onunda sadece okunabilir olarak geliştirilmiş türü olan ReadOnlyMemory structları geliştirilmiştir.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Kaynaklar
http://www.umutozel.com/span-memory
https://docs.microsoft.com/tr-tr/dotnet/api/system.span-1?view=netcore-3.1
https://www.ilkayilknur.com/net-coreda-span-ve-memory-tipleri

Bunlar da hoşunuza gidebilir...

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*