Aracılığıyla paylaş


Yeni C# özelliklerini kullanarak bellek ayırmalarını azaltma

Önemli

Bu bölümde açıklanan teknikler, kodunuzdaki sık erişimli yollara uygulandığında performansı artırır. Sıkça kullanılan yollar, kod tabanınızın normal işlemler sırasında sık ve sürekli olarak yürütülen bölümleridir. Bu tekniklerin genellikle yürütülmeyen kodlara uygulanması en az etkiye sahip olacaktır. Performansı geliştirmek için herhangi bir değişiklik yapmadan önce temeli ölçmek kritik önem taşır. Ardından, bellek performans sorunlarının nerede oluştuğunu belirlemek için bu temeli analiz edin. Tanılama ve izleme bölümünde uygulamanızın performansını ölçmek için birçok platformlar arası araç hakkında bilgi edinebilirsiniz. Visual Studio belgelerinde bellek kullanımını ölçme öğreticisinde profil oluşturma oturumu uygulayabilirsiniz.

Bellek kullanımını ölçtükten ve ayırmaları azaltabileceğinizi belirledikten sonra, ayırmaları azaltmak için bu bölümdeki teknikleri kullanın. Ardışık her değişiklik sonrasında bellek kullanımını yeniden ölçün. Her değişikliğin uygulamanızdaki bellek kullanımı üzerinde olumlu bir etkisi olduğundan emin olun.

.NET'te performans çalışması genellikle kodunuzdan ayırmaların kaldırılması anlamına gelir. Ayırdığınız her bellek bloğunun sonunda serbest olması gerekir. Daha az ayırma, çöp toplamada harcanan süreyi azaltır. Belirli kod yollarından çöp koleksiyonlarını kaldırarak daha öngörülebilir yürütme süresi sağlar.

Ayırımları azaltmak için yaygın bir taktik, kritik veri yapılarını class türlerinden struct türlerine değiştirmektir. Bu değişiklik, bu türlerin kullanılmasının semantiğini etkiler. Parametreler ve dönüşler artık başvuru yerine değere göre geçirilir. Bir değeri kopyalamanın maliyeti, türler küçük, üç sözcük veya daha azsa (bir sözcüğün bir tamsayının doğal boyutunda olduğu düşünüldüğünde) göz ardı edilebilir. Ölçülebilir ve daha büyük türler için gerçek performans etkisine sahip olabilir. Kopyalamanın etkisiyle mücadele etmek için, geliştiriciler bu türleri ref ile geçirerek hedeflenen semantiği geri alabilirler.

C# ref özellikleri, genel kullanılabilirliklerini olumsuz etkilemeden türler için struct istenen semantiği ifade edebilmenizi sağlar. Bu geliştirmelerden önce geliştiricilerin aynı performans etkisini elde etmek için unsafe işaretçiler ve ham bellek ile yapılara başvurması gerekiyordu. Derleyici, yeni ilgili özellikler için ref oluşturur. Doğrulanabilir güvenli kod, derleyicinin olası arabellek taşmalarını veya ayrılmamış ya da serbest bırakılmış belleğe erişmeyi algılaması anlamına gelir. Derleyici bazı hataları algılar ve engeller.

Referans yoluyla geçirme ve döndürme

C# içindeki değişkenler değerleri depolar. Türlerde struct değer, türün bir örneğinin içeriğidir. Türlerde class değer, türün bir örneğini depolayan bir bellek bloğuna başvurudur. Değiştiricinin ref eklenmesi, değişkenin değere başvuruyu depoladığını gösterir. struct türlerinde, referans değeri içeren depolamaya işaret eder. class türlerinde, başvuru, bellek bloğuna olan başvuruyu içeren depolamaya işaret eder.

C# dilinde, yöntemlere yönelik parametreler değere göre geçirilir ve dönüş değerleri değere göre döndürülür. Bağımsız değişkeninin değeri yönteme geçirilir. Dönüş bağımsız değişkeninin değeri , dönüş değeridir.

ref, in, ref readonlyveya out değiştiricisi, bağımsız değişkenin başvuruyla geçirildiğini gösterir. Depolama konumuna bir başvuru yönteme geçirilir. Yöntem imzasına ref eklemek, dönüş değerinin referansla döndürüleceği anlamına gelir. Dönüş değeri, depolama konumuna başvurudur.

Bir değişkenin başka bir değişkene başvurmasını sağlamak için başvuru atamasını da kullanabilirsiniz. Tipik bir atama, sağ taraftaki değeri atamanın sol tarafındaki değişkene kopyalar. Başvuru ataması, sağ taraftaki değişkenin bellek konumunu sol taraftaki değişkene kopyalar. şimdi ref özgün değişkenine başvuruyor:

int anInteger = 42; // assignment.
ref int location = ref anInteger; // ref assignment.
ref int sameLocation = ref location; // ref assignment

Console.WriteLine(location); // output: 42

sameLocation = 19; // assignment

Console.WriteLine(anInteger); // output: 19

Bir değişken atadığınızda değerini değiştirirsiniz. Bir değişkene başvuru atadığınızda, değişkenin referans ettiği şeyi değiştirirsiniz.

Değişkenleri kullanarak ref ile değerlerin depolama alanında doğrudan çalışabilir, referansla geçiş yapabilir ve referans ataması yapabilirsiniz. Derleyici tarafından zorunlu kılınan kapsam kuralları, doğrudan depolamayla çalışırken güvenliği sağlar.

ref readonly ve in değiştiricilerinin her ikisi de bağımsız değişkenin başvuru ile geçirilmesi gerektiğini ve yöntem içinde yeniden atanamayacağını belirtir. Fark, yöntemin ref readonly parametresini değişken olarak kullandığını gösterir. Yöntem, parametreyi yakalayabilir veya salt okunur başvuru ile döndürebilir. Böyle durumlarda değiştiriciyi ref readonly kullanmanız gerekir. Aksi takdirde değiştirici in daha fazla esneklik sunar. parametrenin bağımsız değişkenine değiştiriciyi eklemenize gerek yoktur, bu nedenle mevcut API imzalarını değiştiriciyi kullanarak güvenli bir şekilde güncelleyebilirsiniz.> Ya ref ya da in değiştiricisini bir ref readonly parametresi için bağımsız değişkene eklemezseniz, derleyici bir uyarı verir.

Başvuru güvenli bağlamı

C#, bir ref ifadesine başvurduğu depolama alanının artık geçerli olmadığında erişilememesini sağlamak için ref ifadelere yönelik kurallar içerir. Aşağıdaki örneği göz önünde bulundurun:

public ref int CantEscape()
{
    int index = 42;
    return ref index; // Error: index's ref safe context is the body of CantEscape
}

Derleyici, bir yöntemden yerel değişkene başvuru döndüremediğinizden bir hata bildirir. Çağıran, başvurulmakta olan depolama alanına erişemiyor. Güvenli bağlam, bir ref ifadenin erişilmesinin veya değiştirilmesinin güvenli olduğu kapsamı tanımlar. Aşağıdaki tabloda değişken türleri için başvuru güvenli bağlamları listelenmiştir . ref alanları bir class veya başvuru olmayan struct içinde bildirilemez, bu satırlar bu nedenle tabloda yer almıyor.

Beyanname referans güvenli bağlam
bağlantısız yerel yerel değişkenin bildirildiği blok
ref olmayan parametre geçerli yöntem
ref, ref readonly, in parametresi çağırma yöntemi
out parametresi geçerli yöntem
class alan çağırma yöntemi
referans olmayan struct alan geçerli yöntem
ref alanı ref struct çağırma yöntemi

Bir değişken, eğer ref çağıran yöntemse, döndürülebilir. Başvuru güvenli bağlamı geçerli yöntem veya bir bloksa, ref dönüşe izin verilmez. Aşağıdaki kod parçacığında iki örnek gösterilmektedir. Bir üye alanı, çağıran yöntemin kapsamından erişilebilir, dolayısıyla sınıf veya yapı bir alanının başvuru güvenli bağlamı çağrılan yöntemdir. veya ref değiştiricileri ile bir parametre için in yöntemin tamamıdır. Her ikisi de bir üye yönteminden döndürülebilir ref :

private int anIndex;

public ref int RetrieveIndexRef()
{
    return ref anIndex;
}

public ref int RefMin(ref int left, ref int right)
{
    if (left < right)
        return ref left;
    else
        return ref right;
}

Uyarı

ref readonly veya in değiştiricisi bir parametreye uygulandığında, bu parametre ref readonly tarafından, ref tarafından değil, döndürülebilir.

Derleyici, bir başvurunun ref güvenli bağlamından kaçamamasını sağlar. Derleyici, depolama alanı geçerli olmadığında bir ifadeye erişilebilen kodu yanlışlıkla yazıp yazmadığınız algıladığından, parametreleri, refve ref return yerel değişkenleri güvenli bir ref şekilde kullanabilirsinizref.

Güvenli bağlam ve başvuru yapıları

ref struct türleri, güvenli bir şekilde kullanılabildiklerinden emin olmak için daha fazla kural gerektirir. Bir ref struct tür ref alanları içerebilir. Bunun için güvenli bir bağlamın tanıtılması gerekir. Çoğu tür için güvenli bağlam çağırma yöntemidir. Başka bir deyişle, ref struct olmayan bir değer her zaman bir yöntemden döndürülebilir.

Resmi olmayan şekilde, bir için ref struct, tüm ref alanlarına erişilebilen kapsamdır. Başka bir deyişle, tüm alanlarının ref kesişimidir. Aşağıdaki yöntem bir üye alanına bir ReadOnlySpan<char> döndürür, bu nedenle güvenli bağlamı yöntemidir:

private string longMessage = "This is a long message";

public ReadOnlySpan<char> Safe()
{
    var span = longMessage.AsSpan();
    return span;
}

Buna karşılık, aşağıdaki kod bir hata verir çünkü ref field 'nin Span<int> üyesi yığın üzerinde ayrılmış tamsayı dizisine başvurur. Yönteminden kaçamaz:

public Span<int> M()
{
    int length = 3;
    Span<int> numbers = stackalloc int[length];
    for (var i = 0; i < length; i++)
    {
        numbers[i] = i;
    }
    return numbers; // Error! numbers can't escape this method.
}

Bellek türlerini birleştirme

Tanıtımı System.Span<T> ve System.Memory<T>, bellekle çalışmak için birleşik bir model sağlar. System.ReadOnlySpan<T> ve System.ReadOnlyMemory<T> belleğe erişmek için salt okunur sürümler sağlayın. Tümü, benzer öğelerden oluşan bir diziyi depolayarak bir bellek bloğu üzerinde bir soyutlama sağlar. Fark, Span<T> ve ReadOnlySpan<T>'nin ref struct türleri olması, Memory<T> ve ReadOnlyMemory<T>'ün ise struct türleri olmasıdır. Yayılma alanları bir ref fieldiçerir. Bu nedenle, bir aralığın örnekleri güvenli bağlamını bırakamaz. 'nin güvenli bağlamıref struct, ref güvenli bağlamıdırref field. Memory<T> ve ReadOnlyMemory<T> uygulaması bu kısıtlamayı ortadan kaldırır. Bellek arabelleklerine doğrudan erişmek için bu türleri kullanırsınız.

Ref güvenliği ile performansı geliştirin

Performansı geliştirmek için bu özelliklerin kullanılması şu görevleri içerir:

  • Ayırmalardan kaçının: Bir türü class'den struct'ye dönüştürdüğünüzde, depolanma biçimini değiştirirsiniz. Yerel değişkenler yığında depolanır. Kapsayıcı nesne tahsis edildiğinde üyeler satır içinde depolanır. Bu değişiklik, daha az ayırma anlamına gelir ve bu da çöp toplayıcının yaptığı işi azaltır. Ayrıca, atık toplayıcının daha az sıklıkta çalışması için bellek baskısını azaltabilir.
  • Başvuru semantiğini koruma: Bir türü class'den struct'ye değiştirmek, bir değişkeni bir yönteme geçirme semantiğini değiştirir. Parametrelerinin durumunu değiştiren kodun değiştirilmesi gerekir. Parametresi artık bir structolduğuna göre, yöntemi özgün nesnenin bir kopyasını değiştiriyor. Bu parametreyi parametre ref olarak geçirerek özgün semantiği geri yükleyebilirsiniz. Bu değişiklik sonrasında yöntemi özgün struct dosyayı yeniden değiştirir.
  • Veri kopyalamaktan kaçının: Daha büyük struct türleri kopyalamak bazı kod yollarındaki performansı etkileyebilir. Daha büyük veri yapılarını değer yerine başvuru ile yöntemlere geçirmek için ref değiştiricisini de ekleyebilirsiniz.
  • Değişiklikleri kısıtla: Bir struct tür başvuruyla geçirildiğinde, çağrılan yöntem yapının durumunu değiştirebilir. ref değiştiricisini, argümanın değiştirilemeyeceğini belirtmek için ref readonly veya in değiştiricileriyle değiştirebilirsiniz. Yöntemin parametreyi yakalaması veya salt okunur referans ile döndürmesi durumlarında ref readonly tercih edin. Ayrıca, bir readonly struct'ün hangi üyelerinin değiştirilebileceği üzerinde daha fazla kontrol sağlamak için struct üyelerine sahip readonly veya struct türler oluşturabilirsiniz.
  • Belleği doğrudan işleme: Bazı algoritmalar, veri yapılarını bir dizi öğe içeren bir bellek bloğu olarak ele alırken en verimli şekilde çalışır. Span ve Memory türleri, bellek bloklarına güvenli erişim sağlar.

Bu tekniklerin hiçbiri kod gerektirmez unsafe . Akıllıca kullanıldığında, daha önce yalnızca güvenli olmayan teknikler kullanarak mümkün olan güvenli koddan performans özelliklerini alabilirsiniz. Bellek ayırmalarını azaltmaya yönelik öğretici derste teknikleri kendiniz deneyebilirsiniz.