Bagikan melalui


Tutorial: Mengurangi alokasi memori dengan ref keamanan

Seringkali, penyetelan performa untuk aplikasi .NET melibatkan dua teknik. Pertama, kurangi jumlah dan ukuran alokasi timbunan. Kedua, kurangi seberapa sering data disalin. Visual Studio menyediakan alat hebat yang membantu menganalisis bagaimana aplikasi Anda menggunakan memori. Setelah menentukan tempat aplikasi membuat alokasi yang tidak perlu, Anda membuat perubahan untuk meminimalkan alokasi tersebut. Anda mengubah jenis class menjadi jenis struct. Anda menggunakan reffitur keamanan untuk mempertahankan semantik dan meminimalkan penyalinan tambahan.

Gunakan Visual Studio 17.5 untuk pengalaman terbaik dengan tutorial ini. Alat alokasi objek .NET yang digunakan untuk menganalisis penggunaan memori adalah bagian dari Visual Studio. Anda dapat menggunakan Visual Studio Code dan baris perintah untuk menjalankan aplikasi dan membuat semua perubahan. Namun, Anda tidak akan dapat melihat hasil analisis perubahan Anda.

Aplikasi yang akan Anda gunakan adalah simulasi aplikasi IoT yang memantau beberapa sensor untuk menentukan apakah penyusup telah memasuki galeri rahasia dengan barang berharga. Sensor IoT terus mengirim data yang mengukur campuran Oksigen (O2) dan Karbon Dioksida (CO2) di udara. Mereka juga melaporkan suhu dan kelembaban relatif. Masing-masing nilai ini sedikit berfluktuasi sepanjang waktu. Namun, ketika seseorang memasuki ruangan, perubahan sedikit lebih banyak, dan selalu dalam arah yang sama: Oksigen menurun, Karbon Dioksida meningkat, suhu meningkat, seperti halnya kelembaban relatif. Saat sensor menunjukkan peningkatan secara bersamaan, alarm penyusup akan aktif.

Dalam tutorial ini, Anda akan menjalankan aplikasi, mengambil pengukuran pada alokasi memori, lalu meningkatkan performa dengan mengurangi jumlah alokasi. Kode sumber tersedia di browser sampel.

Jelajahi aplikasi awal

Unduh aplikasi dan jalankan sampel awal. Aplikasi pemula bekerja dengan benar, tetapi karena mengalokasikan banyak objek kecil dengan setiap siklus pengukuran, performanya perlahan-lahan turun saat berjalan dari waktu ke waktu.

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

Banyak baris yang dihapus.

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

Anda dapat menjelajahi kode untuk mempelajari cara kerja aplikasi. Program utama menjalankan simulasi. Setelah Anda menekan <Enter>, sistem membuat ruangan dan mengumpulkan beberapa data dasar awal.

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

Setelah data garis besar tersebut dibuat, ia menjalankan simulasi pada ruangan, di mana generator angka acak menentukan apakah penyusup telah memasuki ruangan:

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

Jenis lain berisi pengukuran, pengukuran debounced yang merupakan rata-rata dari 50 pengukuran terakhir, dan rata-rata semua pengukuran yang diambil.

Selanjutnya, jalankan aplikasi menggunakan alat alokasi objek .NET. Pastikan Anda menggunakan Release build, bukan Debug build. Pada menu Debug , buka profiler Performa. Periksa opsi Pelacakan Alokasi Objek .NET saja, tidak ada yang lain. Jalankan aplikasi Anda hingga selesai. Profiler mengukur alokasi objek dan melaporkan alokasi serta siklus pengolahan sampah. Anda akan melihat grafik yang mirip dengan gambar berikut:

Grafik alokasi untuk menjalankan aplikasi pemberitahuan penyusup sebelum pengoptimalan apa pun.

Grafik sebelumnya menunjukkan bahwa bekerja untuk meminimalkan alokasi akan memberikan manfaat performa. Anda melihat pola sawtooth di grafik objek langsung. Itu memberi tahu Anda bahwa banyak objek dibuat yang dengan cepat menjadi sampah. Mereka kemudian dikumpulkan, seperti yang ditunjukkan dalam grafik delta objek. Bilah merah ke bawah menunjukkan siklus pengumpulan sampah.

Selanjutnya, lihat tab Alokasi di bawah grafik. Tabel ini memperlihatkan jenis apa yang paling banyak dialokasikan:

Bagan yang memperlihatkan tipe mana yang paling sering dialokasikan.

Jenis System.String mempertanggungjawabkan sebagian besar alokasi. Tugas yang paling penting harus meminimalkan frekuensi alokasi string. Aplikasi ini mencetak banyak output berformat ke konsol terus-menerus. Untuk simulasi ini, kami ingin menyimpan pesan, jadi kami akan berkonsentrasi pada dua baris berikutnya: jenis SensorMeasurement, dan jenis IntruderRisk.

Klik dua kali pada SensorMeasurement baris. Anda dapat melihat bahwa semua alokasi berlangsung dalam static metode SensorMeasurement.TakeMeasurement. Anda dapat melihat metode dalam cuplikan berikut:

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

Setiap pengukuran mengalokasikan objek baru SensorMeasurement, yang merupakan jenis class. Setiap SensorMeasurement yang dibuat menyebabkan alokasi timbunan.

Mengubah kelas menjadi struktur

Kode berikut menunjukkan deklarasi awal dari SensorMeasurement:

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

Jenis ini awalnya dibuat sebagai class karena berisi banyak double pengukuran. Ini lebih besar dari yang Anda inginkan untuk menyalin di bagian kritis. Namun, keputusan itu berarti sejumlah besar alokasi. Ubah jenis dari class menjadi struct.

Mengubah dari class untuk struct memperkenalkan beberapa kesalahan pengompilasi karena kode asli menggunakan null pemeriksaan referensi di beberapa tempat. Yang pertama ada di kelas DebounceMeasurement, dalam metode AddMeasurement

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

Jenis DebounceMeasurement mengandung array yang terdiri dari 50 pengukuran. Pembacaan untuk sensor dilaporkan sebagai rata-rata dari 50 pengukuran terakhir. Itu mengurangi kebisingan dalam pembacaan. Sebelum 50 pembacaan penuh dilakukan, nilai-nilai ini adalah null. Kode memeriksa referensi untuk melaporkan rata-rata yang benar pada startup sistem. Setelah mengubah tipe SensorMeasurement menjadi struct, Anda perlu menggunakan pengujian yang berbeda. Tipe SensorMeasurement mencakup string untuk pengidentifikasi ruangan, jadi Anda bisa menggunakan tes tersebut sebagai alternatif.

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

Tiga kesalahan kompilator lainnya semuanya ada dalam metode yang berulang kali mengambil pengukuran di ruangan:

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

Dalam metode awal, variabel lokal untuk SensorMeasurement adalah referensi nullable:

SensorMeasurement? measure = default;

Sekarang setelah SensorMeasurement menjadi struct bukan class, tipe nullable-nya adalah tipe nilai nullable. Anda dapat mengubah deklarasi menjadi jenis nilai untuk memperbaiki kesalahan pengkompilasi yang tersisa:

SensorMeasurement measure = default;

Sekarang setelah kesalahan kompilator telah diatasi, Anda harus memeriksa kode untuk memastikan semantik tidak berubah. Karena struct tipe dikirim berdasarkan nilai, modifikasi yang dilakukan pada parameter metode tidak terlihat setelah metode selesai.

Penting

Mengubah jenis dari suatu class menjadi struct dapat mengubah semantik program Anda. Saat jenis class diteruskan ke metode, setiap mutasi yang dilakukan dalam metode dilakukan pada argumen. Ketika jenis struct diteruskan ke metode, dan mutasi yang dilakukan di dalam metode akan dilakukan pada salinan dari argumen. Itu berarti metode apa pun yang dirancang untuk memodifikasi argumennya harus diperbarui dengan menggunakan pengubah ref pada jenis argumen apa pun yang telah Anda ubah dari class ke struct.

Jenis SensorMeasurement tidak menyertakan metode apa pun yang mengubah status, sehingga tidak menjadi perhatian dalam sampel ini. Anda dapat membuktikan bahwa dengan menambahkan pengubah readonly ke SensorMeasurement struct:

public readonly struct SensorMeasurement

Kompilator menerapkan sifat readonly dari SensorMeasurement struktur. Jika inspeksi kode Anda melewatkan beberapa metode yang memodifikasi status, pengkompilasi akan memberi tahu Anda. Aplikasi Anda masih dibangun tanpa kesalahan, jadi jenis ini adalah readonly. Menambahkan pengubah readonly ketika Anda mengubah jenis dari class ke struct dapat membantu Anda menemukan anggota yang memodifikasi status struct.

Hindari membuat salinan

Anda telah menghapus sejumlah besar alokasi yang tidak perlu dari aplikasi Anda. Jenis SensorMeasurement tidak muncul dalam tabel di mana saja.

Sekarang, sistem melakukan pekerjaan ekstra dengan menyalin struktur SensorMeasurement setiap kali digunakan sebagai parameter atau sebagai nilai pengembalian. Struktur SensorMeasurement berisi empat ganda, DateTime dan string. Struktur itu jelas lebih besar dibandingkan dengan struktur referensi. Mari kita tambahkan pengubah ref atau in ke tempat-tempat di mana SensorMeasurement jenis digunakan.

Langkah selanjutnya adalah menemukan metode yang mengembalikan pengukuran, atau mengambil pengukuran sebagai argumen, dan menggunakan referensi jika memungkinkan. Mulailah di SensorMeasurement struct. Metode statis TakeMeasurement membuat dan mengembalikan yang baru SensorMeasurement:

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

Kita akan membiarkan yang satu ini apa adanya, kembali berdasarkan nilai. Jika Anda mencoba untuk kembali oleh ref, Anda akan mendapatkan kesalahan pengkompilasi. Anda tidak dapat mengembalikan ref ke struktur baru yang dibuat secara lokal dalam metode . Desain struktur yang tidak dapat diubah berarti Anda hanya dapat mengatur nilai pengukuran saat konstruksi. Metode ini harus membuat struktur pengukuran baru.

Mari kita lihat lagi DebounceMeasurement.AddMeasurement. Anda harus menambahkan pengubah in ke measurement parameter:

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

Ini menghemat satu operasi penyalinan. Parameter in adalah referensi ke salinan yang sudah dibuat oleh pemanggil. Anda juga dapat menyimpan salinan menggunakan metode TakeMeasurement dalam jenis Room. Metode ini menggambarkan bagaimana pengkompilasi memberikan keamanan ketika Anda meneruskan argumen dengan ref. Metode awal TakeMeasurement dalam tipe Room menerima argumen Func<SensorMeasurement, bool>. Jika Anda mencoba menambahkan pengubah in atau ref ke deklarasi tersebut, pengkompilasi melaporkan kesalahan. Anda tidak dapat meneruskan ref argumen ke ekspresi lambda. Pengkompilasi tidak dapat menjamin bahwa ekspresi yang disebut tidak menyalin referensi. Jika ekspresi lambda menangkap referensi, referensi dapat memiliki masa pakai lebih lama dari nilai yang dirujuknya. Mengaksesnya di luar konteks aman ref-nya akan mengakibatkan kerusakan memori. Aturan ref keselamatan tidak mengizinkannya. Anda dapat mempelajari lebih lanjut dalam gambaran umum fitur keamanan ref.

Mempertahankan semantik

Serangkaian perubahan akhir tidak akan berdampak besar pada performa aplikasi ini karena jenisnya tidak dibuat di jalur panas. Perubahan ini menggambarkan beberapa teknik lain yang akan Anda gunakan dalam penyetelan performa Anda. Mari kita lihat kelas awal Room :

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

Tipe ini berisi beberapa properti. Beberapa termasuk dalam jenis class. Membuat Room objek melibatkan beberapa alokasi. Satu untuk elemen Room itu sendiri, dan satu untuk setiap anggota tipe class yang dikandungnya. Anda dapat mengonversi dua properti ini dari class jenis ke struct jenis: DebounceMeasurement dan AverageMeasurement jenis. Mari kita kerjakan transformasi tersebut dengan kedua jenis tersebut.

Ubah tipe DebounceMeasurement dari class menjadi struct. Itu menyebabkan kesalahan kompilator CS8983: A 'struct' with field initializers must include an explicitly declared constructor. Anda dapat memperbaikinya dengan menambahkan konstruktor tanpa parameter kosong:

public DebounceMeasurement() { }

Anda dapat mempelajari selengkapnya tentang persyaratan ini di artikel referensi bahasa pada struktur.

Penimpaan Object.ToString() tidak mengubah nilai apa pun dari struktur. Anda dapat menambahkan pengubah ke deklarasi metode tersebut readonly . Jenis DebounceMeasurement ini dapat diubah, jadi Anda perlu berhati-hati agar modifikasi tidak memengaruhi salinan yang sudah dibuang. Metode AddMeasurement ini mengubah status objek. Ini dipanggil dari kelas Room, dalam metode TakeMeasurements. Anda ingin perubahan tersebut tetap ada setelah memanggil metode . Anda dapat mengubah Room.Debounce properti untuk mengembalikan referensi ke satu instans jenis DebounceMeasurement :

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

Ada beberapa perubahan dalam contoh sebelumnya. Pertama, properti adalah properti hanya-baca yang mengembalikan referensi hanya-baca ke instans yang dimiliki oleh ruangan ini. Sekarang didukung oleh field yang dideklarasikan, yang diinisialisasi ketika objek Room dibuat. Setelah membuat perubahan ini, Anda akan memperbarui implementasi AddMeasurement metode. Ini menggunakan bidang backing privat, debounce dan bukan properti readonly Debounce. Dengan begitu, perubahan terjadi pada instans tunggal yang dibuat selama inisialisasi.

Teknik yang sama berfungsi dengan properti Average. Pertama, Anda memodifikasi AverageMeasurement jenis dari class menjadi struct, dan menambahkan readonly pengubah pada metode ToString:

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

Kemudian, Anda memodifikasi Room class mengikuti teknik yang sama dengan yang Anda gunakan untuk Debounce property. Properti Average mengembalikan readonly ref ke bidang privat untuk pengukuran rata-rata. Metode AddMeasurement mengarahkan bidang internal.

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

Hindari peninjuan

Ada satu perubahan terakhir untuk meningkatkan performa. Program utamanya adalah mencetak statistik untuk ruangan, termasuk penilaian risiko:

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

Panggilan ke kotak yang dihasilkan ToString menghasilkan nilai enum. Anda dapat menghindarinya dengan menulis penimpaan di Room kelas yang memformat string berdasarkan nilai perkiraan risiko:

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

Kemudian, ubah kode dalam program utama untuk memanggil metode baru ToString ini:

Console.WriteLine(room.ToString());

Jalankan aplikasi menggunakan profiler dan lihat tabel yang diperbarui untuk alokasi.

Grafik alokasi untuk menjalankan aplikasi pemberitahuan penyusup setelah modifikasi.

Anda telah menghapus banyak alokasi, dan memberi aplikasi Anda peningkatan performa.

Menggunakan keamanan ref dalam aplikasi Anda

Teknik ini adalah penyetelan performa tingkat rendah. Mereka dapat meningkatkan performa dalam aplikasi Anda saat diterapkan ke jalur panas, dan ketika Anda telah mengukur dampak sebelum dan sesudah perubahan. Dalam kebanyakan kasus, siklus yang akan Anda ikuti adalah:

  • Mengukur alokasi: Tentukan jenis apa yang paling dialokasikan, dan kapan Anda dapat mengurangi alokasi timbunan.
  • Konversi kelas ke struct: Sering kali, jenis dapat dikonversi dari class ke struct. Aplikasi Anda menggunakan ruang tumpukan alih-alih membuat alokasi tumpukan.
  • Mempertahankan semantik: Mengonversi ke classstruct dapat memengaruhi semantik untuk parameter dan mengembalikan nilai. Metode apa pun yang memodifikasi parameternya sekarang harus menandai parameter tersebut dengan pengubah ref . Itu memastikan modifikasi dilakukan pada objek yang benar. Demikian pula, jika properti atau nilai pengembalian metode harus dimodifikasi oleh pemanggil, pengembalian tersebut harus ditandai dengan pengubah ref .
  • Hindari salinan: Saat Anda meneruskan struct besar sebagai parameter, Anda dapat menandai parameter dengan pengubah in . Anda dapat meneruskan referensi dalam lebih sedikit byte, dan memastikan bahwa metode tidak mengubah nilai asli. Anda juga dapat mengembalikan nilai dengan readonly ref untuk mengembalikan referensi yang tidak dapat dimodifikasi.

Dengan menggunakan teknik ini, Anda dapat meningkatkan performa di jalur panas kode Anda.