Öğretici: Güvenlikle ref bellek ayırmalarını azaltma

.NET uygulaması için performans ayarlama işlemi genellikle iki teknik içerir. İlk olarak, yığın ayırmalarının sayısını ve boyutunu azaltın. İkinci olarak, verilerin kopyalanan sıklık oranını azaltın. Visual Studio, uygulamanızın belleği nasıl kullandığını analiz etmek için harika araçlar sağlar. Uygulamanızın gereksiz ayırmaları nerede yaptığını belirledikten sonra, bu ayırmaları en aza indirmek için değişiklikler yaparsınız. Türleri türlere struct dönüştürürsinizclass. Semantiği korumak ve ek kopyalamayı en aza indirmek için güvenlik özelliklerini kullanırsınızref.

Bu öğreticide en iyi deneyim için Visual Studio 17.5'i kullanın. Bellek kullanımını analiz etmek için kullanılan .NET nesne ayırma aracı Visual Studio'nun bir parçasıdır. Uygulamayı çalıştırmak ve tüm değişiklikleri yapmak için Visual Studio Code'u ve komut satırını kullanabilirsiniz. Ancak, değişikliklerinizin analiz sonuçlarını göremezsiniz.

Kullanacağınız uygulama, yetkisiz bir kullanıcının değerli eşyalarla gizli bir galeriye girip girmediğini belirlemek için çeşitli algılayıcıları izleyen bir IoT uygulamasının simülasyonudur. IoT algılayıcıları sürekli olarak oksijen (O2) ve karbondioksit (CO2) karışımını ölçen veriler gönderir. Ayrıca sıcaklığı ve göreli nemi de bildirirler. Bu değerlerin her biri her zaman biraz dalgalanıyor. Ancak, bir kişi odaya girdiğinde, değişiklik biraz daha fazla ve her zaman aynı yöndedir: Oksijen azalır, KarbonDioksit artar, sıcaklık artar, göreli nem gibi. Algılayıcılar artacak şekilde bir araya geldiğinde davetsiz misafir alarmı tetikleniyor.

Bu öğreticide uygulamayı çalıştıracak, bellek ayırmalarıyla ilgili ölçümler alacak, ardından ayırma sayısını azaltarak performansı geliştireceksiniz. Kaynak kodu örnekler tarayıcısında kullanılabilir.

Başlangıç uygulamasını keşfetme

Uygulamayı indirin ve başlangıç örneğini çalıştırın. Başlangıç uygulaması düzgün çalışır, ancak her ölçüm döngüsünde çok sayıda küçük nesne ayırdığından, zaman içinde çalıştığından performansı yavaş yavaş düşer.

Press <return> to start simulation

Debounced measurements:
    Temp:      67.332
    Humidity:  41.077%
    Oxygen:    21.097%
    CO2 (ppm): 404.906
Average measurements:
    Temp:      67.332
    Humidity:  41.077%
    Oxygen:    21.097%
    CO2 (ppm): 404.906

Debounced measurements:
    Temp:      67.349
    Humidity:  46.605%
    Oxygen:    20.998%
    CO2 (ppm): 408.707
Average measurements:
    Temp:      67.349
    Humidity:  46.605%
    Oxygen:    20.998%
    CO2 (ppm): 408.707

Birçok satır kaldırıldı.

Debounced measurements:
    Temp:      67.597
    Humidity:  46.543%
    Oxygen:    19.021%
    CO2 (ppm): 429.149
Average measurements:
    Temp:      67.568
    Humidity:  45.684%
    Oxygen:    19.631%
    CO2 (ppm): 423.498
Current intruders: 3
Calculated intruder risk: High

Debounced measurements:
    Temp:      67.602
    Humidity:  46.835%
    Oxygen:    19.003%
    CO2 (ppm): 429.393
Average measurements:
    Temp:      67.568
    Humidity:  45.684%
    Oxygen:    19.631%
    CO2 (ppm): 423.498
Current intruders: 3
Calculated intruder risk: High

Uygulamanın nasıl çalıştığını öğrenmek için kodu inceleyebilirsiniz. Ana program simülasyonu çalıştırır. tuşuna bastıktan <Enter>sonra bir oda oluşturur ve bazı ilk temel verileri toplar:

Console.WriteLine("Press <return> to start simulation");
Console.ReadLine();
var room = new Room("gallery");
var r = new Random();

int counter = 0;

room.TakeMeasurements(
    m =>
    {
        Console.WriteLine(room.Debounce);
        Console.WriteLine(room.Average);
        Console.WriteLine();
        counter++;
        return counter < 20000;
    });

Bu temel veriler oluşturulduktan sonra, rastgele bir sayı oluşturucunun odaya izinsiz giren bir kullanıcının girip girmediğini belirlediği odada simülasyonu çalıştırır:

counter = 0;
room.TakeMeasurements(
    m =>
    {
        Console.WriteLine(room.Debounce);
        Console.WriteLine(room.Average);
        room.Intruders += (room.Intruders, r.Next(5)) switch
        {
            ( > 0, 0) => -1,
            ( < 3, 1) => 1,
            _ => 0
        };

        Console.WriteLine($"Current intruders: {room.Intruders}");
        Console.WriteLine($"Calculated intruder risk: {room.RiskStatus}");
        Console.WriteLine();
        counter++;
        return counter < 200000;
    });

Diğer türlerde ölçüler, son 50 ölçümün ortalaması olan birbounced ölçümü ve alınan tüm ölçümlerin ortalaması yer alır.

Ardından, .NET nesne ayırma aracını kullanarak uygulamayı çalıştırın. Derlemeyi değil derlemeyi Release kullandığınızdan Debug emin olun. Hata Ayıkla menüsünde Performans profili oluşturucuyu açın. .NET Nesne Ayırma İzleme seçeneğini işaretleyin, ancak başka bir şey yok. Tamamlanmak için uygulamanızı çalıştırın. Profil oluşturucu, nesne ayırmalarını ölçer ve ayırmaları ve çöp toplama döngülerini raporlar. Aşağıdaki görüntüye benzer bir grafik görmeniz gerekir:

Allocation graph for running the intruder alert app before any optimizations.

Önceki grafikte ayırmaları en aza indirmek için çalışmanın performans avantajları sağlayacağı gösterilmektedir. Canlı nesneler grafiğinde sawtooth deseni görürsünüz. Bu, hızlı bir şekilde çöpe dönüşen çok sayıda nesnenin oluşturulduğunu bildirir. Bunlar daha sonra nesne delta grafiğinde gösterildiği gibi toplanır. Aşağı doğru kırmızı çubuklar çöp toplama döngüsünü gösterir.

Ardından, grafiklerin altındaki Ayırmalar sekmesine bakın. Bu tablo en çok hangi türlerin ayrıldığını gösterir:

Chart that shows which types are allocated most frequently.

Türü System.String en fazla ayırmayı hesaplar. En önemli görev, dize ayırma sıklığını en aza indirmek olmalıdır. Bu uygulama, konsola sürekli olarak çok sayıda biçimlendirilmiş çıktı yazdırır. Bu benzetim için iletileri tutmak istediğimiz için sonraki iki satıra odaklanacağız: SensorMeasurement tür ve IntruderRisk tür.

Satıra SensorMeasurement çift tıklayın. Tüm ayırmaların yönteminde SensorMeasurement.TakeMeasurementgerçekleştiğini static görebilirsiniz. Yöntemini aşağıdaki kod parçacığında görebilirsiniz:

public static SensorMeasurement TakeMeasurement(string room, int intruders)
{
    return new SensorMeasurement
    {
        CO2 = (CO2Concentration + intruders * 10) + (20 * generator.NextDouble() - 10.0),
        O2 = (O2Concentration - intruders * 0.01) + (0.005 * generator.NextDouble() - 0.0025),
        Temperature = (TemperatureSetting + intruders * 0.05) + (0.5 * generator.NextDouble() - 0.25),
        Humidity = (HumiditySetting + intruders * 0.005) + (0.20 * generator.NextDouble() - 0.10),
        Room = room,
        TimeRecorded = DateTime.Now
    };
}

Her ölçüm türü olan yeni SensorMeasurement bir class nesne ayırır. Oluşturulan her SensorMeasurement biri yığın ayırmaya neden olur.

Sınıfları yapılara değiştirme

Aşağıdaki kod, öğesinin ilk bildirimini SensorMeasurementgösterir:

public class SensorMeasurement
{
    private static readonly Random generator = new Random();

    public static SensorMeasurement TakeMeasurement(string room, int intruders)
    {
        return new SensorMeasurement
        {
            CO2 = (CO2Concentration + intruders * 10) + (20 * generator.NextDouble() - 10.0),
            O2 = (O2Concentration - intruders * 0.01) + (0.005 * generator.NextDouble() - 0.0025),
            Temperature = (TemperatureSetting + intruders * 0.05) + (0.5 * generator.NextDouble() - 0.25),
            Humidity = (HumiditySetting + intruders * 0.005) + (0.20 * generator.NextDouble() - 0.10),
            Room = room,
            TimeRecorded = DateTime.Now
        };
    }

    private const double CO2Concentration = 409.8; // increases with people.
    private const double O2Concentration = 0.2100; // decreases
    private const double TemperatureSetting = 67.5; // increases
    private const double HumiditySetting = 0.4500; // increases

    public required double CO2 { get; init; }
    public required double O2 { get; init; }
    public required double Temperature { get; init; }
    public required double Humidity { get; init; }
    public required string Room { get; init; }
    public required DateTime TimeRecorded { get; init; }

    public override string ToString() => $"""
            Room: {Room} at {TimeRecorded}:
                Temp:      {Temperature:F3}
                Humidity:  {Humidity:P3}
                Oxygen:    {O2:P3}
                CO2 (ppm): {CO2:F3}
            """;
}

Türü başlangıçta çok sayıda double ölçüm içerdiğinden olarak class oluşturulmuştur. Sık erişimli yollarda kopyalamak istediğinizden daha büyük. Ancak bu karar çok sayıda ayırma anlamına geliyordu. türünü classstructolarak değiştirin.

başvuru kullanılan null özgün kod birkaç noktada denetlediğinden , classstruct olarak değiştirerek birkaç derleyici hatasına neden olur. birincisi sınıfında, yöntemindedir DebounceMeasurementAddMeasurement :

public void AddMeasurement(SensorMeasurement datum)
{
    int index = totalMeasurements % debounceSize;
    recentMeasurements[index] = datum;
    totalMeasurements++;
    double sumCO2 = 0;
    double sumO2 = 0;
    double sumTemp = 0;
    double sumHumidity = 0;
    for (int i = 0; i < debounceSize; i++)
    {
        if (recentMeasurements[i] is not null)
        {
            sumCO2 += recentMeasurements[i].CO2;
            sumO2+= recentMeasurements[i].O2;
            sumTemp+= recentMeasurements[i].Temperature;
            sumHumidity += recentMeasurements[i].Humidity;
        }
    }
    O2 = sumO2 / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
    CO2 = sumCO2 / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
    Temperature = sumTemp / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
    Humidity = sumHumidity / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
}

Türü DebounceMeasurement 50 ölçümden oluşan bir dizi içerir. Bir algılayıcının okumaları, son 50 ölçümün ortalaması olarak bildirilir. Bu, okumalardaki gürültüyü azaltır. Tam 50 okuma alınmadan önce, bu değerler şeklindedir null. Kod, sistem başlangıcında doğru ortalamayı raporlamak için null başvuruyu denetler. Türü bir yapıya SensorMeasurement değiştirdikten sonra farklı bir test kullanmanız gerekir. Tür SensorMeasurement , oda tanımlayıcısı için bir string içerir, bu nedenle bunun yerine bu testi kullanabilirsiniz:

if (recentMeasurements[i].Room is not null)

Diğer üç derleyici hatasının tümü, bir odada tekrar tekrar ölçümler alan yöntemdedir:

public void TakeMeasurements(Func<SensorMeasurement, bool> MeasurementHandler)
{
    SensorMeasurement? measure = default;
    do {
        measure = SensorMeasurement.TakeMeasurement(Name, Intruders);
        Average.AddMeasurement(measure);
        Debounce.AddMeasurement(measure);
    } while (MeasurementHandler(measure));
}

starter yönteminde, öğesinin SensorMeasurement yerel değişkeni null atanabilir bir başvurudur:

SensorMeasurement? measure = default;

artık yerine bir classstruct olduğuna göre SensorMeasurement null atanabilir, null atanabilir bir değer türüdür. Bildirimi bir değer türüyle değiştirerek kalan derleyici hatalarını düzeltebilirsiniz:

SensorMeasurement measure = default;

Artık derleyici hataları giderildiğine göre, semantiğin değişmediğinden emin olmak için kodu incelemeniz gerekir. struct Türler değere göre geçirildiğinden, yöntem parametrelerinde yapılan değişiklikler yöntem döndürdüğünde görünmez.

Önemli

Bir türü olarak classstruct değiştirmek, programınızın semantiğini değiştirebilir. Bir yönteme bir class tür geçirildiğinde, yöntemde yapılan tüm mutasyonlar bağımsız değişkene yapılır. Bir yönteme bir struct tür geçirildiğinde ve yöntemde yapılan mutasyonlar bağımsız değişkenin bir kopyasına geçirilir. Bu, bağımsız değişkenlerini tasarıma göre değiştiren herhangi bir yöntemin ref , bir bağımsız değişken türünde değiştirdiğiniz class herhangi bir structbağımsız değişken türünde değiştiriciyi kullanacak şekilde güncelleştirilmesi gerektiği anlamına gelir.

Türü, SensorMeasurement durumu değiştiren herhangi bir yöntem içermez, bu nedenle bu örnekte sorun olmaz. Değiştiriciyi yapıya readonlySensorMeasurement ekleyerek bunu kanıtlayabilirsiniz:

public readonly struct SensorMeasurement

Derleyici, yapının yapısını SensorMeasurement zorlarreadonly. Kodu incelemeniz durumu değiştiren bir yöntemi kaçırdıysa, derleyici size bildirir. Uygulamanız hala hatasız derlendiğinden bu tür olur readonly. bir türü olarak structclass değiştirdiğinizde değiştiriciyi readonly eklemek, durumunu değiştiren üyeleri bulmanıza structyardımcı olabilir.

Kopya oluşturmaktan kaçının

Uygulamanızdan çok sayıda gereksiz ayırma kaldırdınız. Tür SensorMeasurement tabloda hiçbir yerde görünmez.

Şimdi, parametre veya dönüş değeri olarak her kullanıldığında yapıyı SensorMeasurement kopyalamak için fazladan çalışma yapıyor. YapısıSensorMeasurement, a ve stringolmak DateTime üzere dört çift içerir. Bu yapı bir başvurudan ölçülebilir derecede büyüktür. Türün ref kullanıldığı yerlere veya in değiştiricilerini SensorMeasurement ekleyelim.

Sonraki adım, ölçü döndüren veya bir ölçümü bağımsız değişken olarak alan yöntemleri bulmak ve mümkün olduğunca başvuruları kullanmaktır. Yapıdan SensorMeasurement başlayın. Statik TakeMeasurement yöntem yeni SensorMeasurementbir oluşturur ve döndürür:

public static SensorMeasurement TakeMeasurement(string room, int intruders)
{
    return new SensorMeasurement
    {
        CO2 = (CO2Concentration + intruders * 10) + (20 * generator.NextDouble() - 10.0),
        O2 = (O2Concentration - intruders * 0.01) + (0.005 * generator.NextDouble() - 0.0025),
        Temperature = (TemperatureSetting + intruders * 0.05) + (0.5 * generator.NextDouble() - 0.25),
        Humidity = (HumiditySetting + intruders * 0.005) + (0.20 * generator.NextDouble() - 0.10),
        Room = room,
        TimeRecorded = DateTime.Now
    };
}

Değere göre döndürerek bunu olduğu gibi bırakacağız. tarafından refdöndürülmeye çalışsaydınız derleyici hatasıyla karşılaşırsınız. yönteminde yerel olarak oluşturulan yeni bir yapıya döndüremezsiniz ref . Sabit yapının tasarımı, yalnızca inşaatta ölçümün değerlerini ayarlayabildiğiniz anlamına gelir. Bu yöntem yeni bir ölçüm yapısı oluşturmalıdır.

'a tekrar bakalım DebounceMeasurement.AddMeasurement. Değiştiriciyi in parametresine measurement eklemelisiniz:

public void AddMeasurement(in SensorMeasurement datum)
{
    int index = totalMeasurements % debounceSize;
    recentMeasurements[index] = datum;
    totalMeasurements++;
    double sumCO2 = 0;
    double sumO2 = 0;
    double sumTemp = 0;
    double sumHumidity = 0;
    for (int i = 0; i < debounceSize; i++)
    {
        if (recentMeasurements[i].Room is not null)
        {
            sumCO2 += recentMeasurements[i].CO2;
            sumO2+= recentMeasurements[i].O2;
            sumTemp+= recentMeasurements[i].Temperature;
            sumHumidity += recentMeasurements[i].Humidity;
        }
    }
    O2 = sumO2 / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
    CO2 = sumCO2 / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
    Temperature = sumTemp / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
    Humidity = sumHumidity / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
}

Bu, bir kopyalama işlemini kaydeder. parametresi, in çağıranın önceden oluşturduğu kopyaya bir başvurudur. Türündeki TakeMeasurementRoom yöntemiyle bir kopyasını da kaydedebilirsiniz. Bu yöntem, tarafından refbağımsız değişkenleri geçirirken derleyicinin nasıl güvenlik sağladığını gösterir. türündeki Room ilk TakeMeasurement yöntem, bağımsız değişkenini Func<SensorMeasurement, bool>alır. Veya ref değiştiricisini in bu bildirime eklemeye çalışırsanız, derleyici bir hata bildirir. Lambda ifadesine bağımsız değişken geçiremezsiniz ref . Derleyici, çağrılan ifadenin başvuruyu kopyalamadığını garanti etmez. Lambda ifadesi başvuruyu yakalarsa , başvurunun ömrü başvurduğu değerden daha uzun olabilir. Başvuru güvenli bağlamı dışında erişim bellek bozulmasına neden olabilir. Güvenlik ref kuralları buna izin vermiyor. Başvuru güvenliği özelliklerine genel bakış bölümünden daha fazla bilgi edinebilirsiniz.

Semantiği koruma

Türler sık erişimli yollarda oluşturulmadığından, son değişiklik kümelerinin bu uygulamanın performansı üzerinde önemli bir etkisi olmayacaktır. Bu değişiklikler, performans ayarlama işleminizde kullanacağınız diğer tekniklerden bazılarını gösterir. İlk Room sınıfa göz atalım:

public class Room
{
    public AverageMeasurement Average { get; } = new ();
    public DebounceMeasurement Debounce { get; } = new ();
    public string Name { get; }

    public IntruderRisk RiskStatus
    {
        get
        {
            var CO2Variance = (Debounce.CO2 - Average.CO2) > 10.0 / 4;
            var O2Variance = (Average.O2 - Debounce.O2) > 0.005 / 4.0;
            var TempVariance = (Debounce.Temperature - Average.Temperature) > 0.05 / 4.0;
            var HumidityVariance = (Debounce.Humidity - Average.Humidity) > 0.20 / 4;
            IntruderRisk risk = IntruderRisk.None;
            if (CO2Variance) { risk++; }
            if (O2Variance) { risk++; }
            if (TempVariance) { risk++; }
            if (HumidityVariance) { risk++; }
            return risk;
        }
    }

    public int Intruders { get; set; }

    
    public Room(string name)
    {
        Name = name;
    }

    public void TakeMeasurements(Func<SensorMeasurement, bool> MeasurementHandler)
    {
        SensorMeasurement? measure = default;
        do {
            measure = SensorMeasurement.TakeMeasurement(Name, Intruders);
            Average.AddMeasurement(measure);
            Debounce.AddMeasurement(measure);
        } while (MeasurementHandler(measure));
    }
}

Bu tür çeşitli özellikler içerir. Bazıları türleridir class . Room Nesne oluşturmak için birden çok ayırma gerekir. Biri kendisi için Room , diğeri de içerdiği bir türün her üyesi class için. Bu özelliklerden ikisini türlerden class türlere struct dönüştürebilirsiniz: DebounceMeasurement ve AverageMeasurement türleri. Şimdi bu dönüştürmeyi her iki türle de inceleyelim.

türünü DebounceMeasurementclassstructolarak değiştirin. Bu, bir derleyici hatasına CS8983: A 'struct' with field initializers must include an explicitly declared constructorneden olur. Boş bir parametresiz oluşturucu ekleyerek bunu düzeltebilirsiniz:

public DebounceMeasurement() { }

Bu gereksinim hakkında daha fazla bilgiyi yapılarla ilgili dil başvurusu makalesinde bulabilirsiniz.

Geçersiz Object.ToString() kılma, yapının değerlerinden hiçbirini değiştirmez. Değiştiriciyi readonly bu yöntem bildirimine ekleyebilirsiniz. Türü DebounceMeasurement değişebilir, bu nedenle değişikliklerin atılan kopyaları etkilememesi için dikkatli olmanız gerekir. AddMeasurement yöntemi nesnenin durumunu değiştirir. yönteminde Room sınıfından çağrılır TakeMeasurements . Yöntemini çağırdıktan sonra bu değişikliklerin kalıcı olmasını istiyorsunuz. Özelliğini, türün Room.Debounce tek bir örneğine DebounceMeasurement başvuru döndürecek şekilde değiştirebilirsiniz:

private DebounceMeasurement debounce = new();
public ref readonly DebounceMeasurement Debounce { get { return ref debounce; } }

Önceki örnekte birkaç değişiklik var. İlk olarak özelliği, bu odanın sahip olduğu örneğe salt okunur bir başvuru döndüren salt okunur bir özelliktir. Artık nesne örneği oluşturulurken Room başlatılan bildirilen bir alan tarafından destekleniyor. Bu değişiklikleri yaptıktan sonra yönteminin AddMeasurement uygulamasını güncelleştireceksiniz. Salt okunur özelliğini Debouncedeğil, debounceözel yedekleme alanını kullanır. Bu şekilde, değişiklikler başlatma sırasında oluşturulan tek örnekte gerçekleşir.

Aynı teknik özelliğiyle Average de çalışır. İlk olarak, türünden AverageMeasurementclass ' structa değiştirirsiniz ve yöntemine readonly değiştiriciyi ToString eklersiniz:

namespace IntruderAlert;

public struct AverageMeasurement
{
    private double sumCO2 = 0;
    private double sumO2 = 0;
    private double sumTemperature = 0;
    private double sumHumidity = 0;
    private int totalMeasurements = 0;

    public AverageMeasurement() { }

    public readonly double CO2 => sumCO2 / totalMeasurements;
    public readonly double O2 => sumO2 / totalMeasurements;
    public readonly double Temperature => sumTemperature / totalMeasurements;
    public readonly double Humidity => sumHumidity / totalMeasurements;

    public void AddMeasurement(in SensorMeasurement datum)
    {
        totalMeasurements++;
        sumCO2 += datum.CO2;
        sumO2 += datum.O2;
        sumTemperature += datum.Temperature;
        sumHumidity+= datum.Humidity;
    }

    public readonly override string ToString() => $"""
        Average measurements:
            Temp:      {Temperature:F3}
            Humidity:  {Humidity:P3}
            Oxygen:    {O2:P3}
            CO2 (ppm): {CO2:F3}
        """;
}

Ardından, sınıfını Room özelliği için kullandığınız teknikle Debounce değiştirirsiniz. özelliği, Average ortalama ölçüm için özel alana bir readonly ref döndürür. AddMeasurement yöntemi iç alanları değiştirir.

private AverageMeasurement average = new();
public  ref readonly AverageMeasurement Average { get { return ref average; } }

Boks yapmaktan kaçının

Performansı geliştirmek için son bir değişiklik vardır. Ana program, risk değerlendirmesi dahil olmak üzere odanın istatistiklerini yazdırmaktır:

Console.WriteLine($"Current intruders: {room.Intruders}");
Console.WriteLine($"Calculated intruder risk: {room.RiskStatus}");

Oluşturulan ToString kutulara yapılan çağrı, sabit listesi değerini gösterir. Dizeyi Room tahmini risk değerine göre biçimlendiren sınıfında bir geçersiz kılma yazarak bunu önleyebilirsiniz:

public override string ToString() =>
    $"Calculated intruder risk: {RiskStatus switch
    {
        IntruderRisk.None => "None",
        IntruderRisk.Low => "Low",
        IntruderRisk.Medium => "Medium",
        IntruderRisk.High => "High",
        IntruderRisk.Extreme => "Extreme",
        _ => "Error!"
    }}, Current intruders: {Intruders.ToString()}";

Ardından, ana programdaki kodu şu yeni ToString yöntemi çağıracak şekilde değiştirin:

Console.WriteLine(room.ToString());

Profil oluşturucuyu kullanarak uygulamayı çalıştırın ve ayırmalar için güncelleştirilmiş tabloya bakın.

Allocation graph for running the intruder alert app after modifications.

Çok sayıda ayırmayı kaldırdınız ve uygulamanıza bir performans artışı sağladınız.

Uygulamanızda başvuru güvenliğini kullanma

Bu teknikler düşük düzeyli performans ayarlamalarıdır. Bunlar, sık erişimli yollara uygulandığında ve değişikliklerin öncesindeki ve sonrasındaki etkiyi ölçtünüzde uygulamanızdaki performansı artırabilir. Çoğu durumda izleyeceğiniz döngü şudur:

  • Ölçü ayırmaları: En çok hangi türlerin ayrıldığını ve yığın ayırmalarını ne zaman azaltabileceğinizi belirleyin.
  • Sınıfı yapıya dönüştürme: Birçok kez, türler öğesinden öğesine classstructdönüştürülebilir. Uygulamanız yığın ayırmaları yapmak yerine yığın alanı kullanıyor.
  • Semantiği koruma: ' class yi öğesine struct dönüştürmek, parametrelerin ve dönüş değerlerinin semantiğini etkileyebilir. Parametrelerini değiştiren herhangi bir yöntem artık bu parametreleri değiştirici ile ref işaretlemelidir. Bu, değişikliklerin doğru nesnede yapılmasını sağlar. Benzer şekilde, bir özellik veya yöntem dönüş değeri çağıran tarafından değiştirilmesi gerekiyorsa, bu dönüş değiştirici ile ref işaretlenmelidir.
  • Kopyalardan kaçının: Büyük bir yapıyı parametre olarak geçirdiğinizde, parametresini in değiştirici ile işaretleyebilirsiniz. Bir başvuruyu daha az bayt olarak geçirebilir ve yöntemin özgün değeri değiştirmediğinden emin olabilirsiniz. Değiştirilmeyen bir başvuru döndürmek için ile de değer readonly ref döndürebilirsiniz.

Bu teknikleri kullanarak kodunuzun sık erişimli yollarındaki performansı geliştirebilirsiniz.