Aracılığıyla paylaş


Kılavuz: ref güvenliği ile bellek ayırmalarını azaltma

.NET uygulaması için performans ayarlama işlemi genellikle iki teknik içerir. İlk olarak, yığın tahsislerinin 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. class türlerini struct türlerine dönüştürürsünüz. Semantiği korumak ve ek kopyalamayı en aza indirmek için güvenlik ref kullanırsınız.

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 adet ölçümün ortalaması olan yumuşatılmış bir ö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. Release tasarımını kullandığınızdan ve Debug tasarımını kullanmadığınızdan 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 hiçbir şeyi işaretlemeyin. Uygulamanızı tamamlanana kadar çalıştırın. Profilleyici, nesne tahsislerini ölçer ve tahsisler ile çöp toplama döngülerini raporlar. Aşağıdaki görüntüye benzer bir grafik görmeniz gerekir:

Herhangi bir iyileştirmeden önce davetsiz misafir uyarı uygulamasını çalıştırmak için ayırma grafiği.

Ö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:

Hangi türlerin en sık ayrıldığını gösteren grafik.

System.String türü en fazla ayırımı yapan olarak hesaplanır. En önemli görev, dize tahsisleri 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 simülasyon için mesajları tutmak istediğimizden, sonraki iki satıra odaklanacağız: SensorMeasurement türü ve IntruderRisk türü.

SensorMeasurement satırına çift tıklayın. Tüm ayırmaların static yönteminde SensorMeasurement.TakeMeasurement gerçekleştiğini 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, yeni bir SensorMeasurement nesnesi ayırır, bu nesne bir class türüdür. Oluşturulan her SensorMeasurement yığın ayırmaya neden olur.

Sınıfları yapılara dönüştürme

Aşağıdaki kod, SensorMeasurement öğesinin ilk bildirimini gö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 class ölçüm içerdiği için double olarak 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ü class'den struct'e değiştirin.

class'den struct'e geçiş yapmak, özgün kodun birkaç noktada null referans kontrolleri kullandığı için birkaç derleyici hatasına neden olur. İlki DebounceMeasurement sınıfında, AddMeasurement yöntemindedir.

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));
}

başlatıcı metotta, öğesinin SensorMeasurement yerel değişkeni null geçişli bir referanstır:

SensorMeasurement? measure = default;

Şimdi SensorMeasurement bir struct yerine bir class olduğuna göre, null atanabilir bir değer türü olan vardı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ü class'den struct'e 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 struct türü bir metoda geçirildiğinde, metotta yapılan değişiklikler, argümanın bir kopyası üzerinde yapılır. Bu, tasarım gereği argümanlarını değiştiren herhangi bir yöntemin, değiştirdiğiniz herhangi bir ref bağımsız değişken türü için, class bağımsız değişken türü yerine struct değiştiricisini kullanacak şekilde güncellenmesi 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 readonly yapının doğasını SensorMeasurement sağlar. Kodu incelemeniz durumu değiştiren bir yöntemi kaçırdıysa, derleyici size bildirir. Uygulamanız hala hatasız bir şekilde derleniyor, bu tür readonly'dir. Bir türü readonly'den class'ye değiştirirken struct değiştiricisini eklemek, struct durumunu değiştiren üyeleri bulmanıza yardı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, her parametre veya dönüş değeri olarak kullanıldığında SensorMeasurement yapısını kopyalamak için fazladan iş yapmakta. Yapı SensorMeasurement dört adet double ve bir DateTime, bir string içerir. Bu yapı bir referanstan ölçülebilir derecede daha büyüktür. ref türünün kullanıldığı yerlere in veya SensorMeasurement değiştiricilerini 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ı SensorMeasurement içinde başlayın. Statik TakeMeasurement yöntemi bir SensorMeasurement yeni 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. Eğer ref kullanarak döndürmeye çalışsaydınız, bir derleyici hatası alırdınız. Yerel olarak yöntem içinde oluşturulan yeni bir yapıya ref geri döndüremezsiniz. 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. in değiştiricisini measurement parametresine 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 önler. parametresi, in çağıranın önceden oluşturduğu kopyaya bir başvurudur. TakeMeasurement türündeki Room yöntemiyle bir kopyasını da kaydedebilirsiniz. Bu yöntem, ref tarafından bağımsız değişkenleri geçirirken derleyicinin nasıl güvenlik sağladığını gösterir. türündeki TakeMeasurement içindeki başlangıç Room yöntemi, bir Func<SensorMeasurement, bool> bağımsız değişken alır. Eğer bu bildirime in veya ref değiştiricisini eklemeye çalışırsanız, derleyici bir hata rapor eder. ref bağımsız değişkenini bir lambda ifadesine geçiremezsiniz. 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. Referans güvenli bağlamı dışındaki erişimler bellek bozulmasına neden olabilir. Güvenlik ref kuralları buna izin vermiyor. Referans güvenliği özelliklerine genel bakış bölümünden daha fazla bilgi edinebilirsiniz.

Semantiği koruma

Türler yoğun işlem yollarında 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ı class türleridir. Bir Room nesnesi oluşturmak, birden fazla tahsis gerektirir. Birisi Room'ün kendisi için ve içerdiği bir class türünün her bir üyesi için bir tane. 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.

Değiştirin DebounceMeasurement türünü class'den struct'ye. 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 kılma Object.ToString() yapının değerlerinden hiçbirini değiştirmez. Değiştiriciyi readonly bu yöntem bildirimine ekleyebilirsiniz. Türü DebounceMeasurementdeğ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. Room sınıfından, TakeMeasurements yönteminde çağrılır. Yöntemini çağırdıktan sonra bu değişikliklerin kalıcı olmasını istiyorsunuz. Room.Debounce özelliğini, türünün tek bir örneğine DebounceMeasurement 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, özellik 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 AddMeasurement yönteminin uygulamasını güncelleştireceksiniz. Özel yedekleme alanı debounce kullanılır, salt okunur özelliği Debounce değil. Bu şekilde, değişiklikler başlatma sırasında oluşturulan tek örnekte gerçekleşir.

Aynı teknik Average özelliğiyle de çalışır. Önce, AverageMeasurement türünü class 'tan struct'e değiştirir ve readonly yöntemine ToString değiştiriciyi 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, Room sınıfını Debounce özelliği için kullandığınız teknikle değiştirirsiniz. Bu Average özelliği, ortalama ölçüm için özel alana bir readonly ref referansı 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.

Değişikliklerden sonra davetsiz misafir uyarı uygulamasını çalıştırmak için ayırma grafiği.

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

Uygulamanızda referans 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:

  • Tahsis ölçümleri: En fazla tahsis edilen türlerin neler olduğunu ve yığın tahsislerini ne zaman azaltabileceğinizi belirleyin.
  • Sınıfı yapıya dönüştür: Çoğu zaman, türler bir class türünden bir struct türüne dö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 referansı daha az baytla aktarabilir ve yöntemin orijinal değeri değiştirmediğinden emin olabilirsiniz. Modifiye edilemeyecek bir başvuru döndürmek için ayrıca readonly ref ile değer döndürebilirsiniz.

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