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 mengonversi class
jenis ke struct
jenis. Anda menggunakan ref
fitur 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. Ketika sensor digabungkan untuk menunjukkan peningkatan, alarm penyusup dipicu.
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.
Menjelajahi aplikasi pemula
Unduh aplikasi dan jalankan sampel pemula. 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>
, itu membuat ruangan, dan mengumpulkan beberapa data garis besar 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, tetapi tidak ada yang lain. Jalankan aplikasi Anda hingga selesai. Profiler mengukur alokasi objek dan laporan tentang alokasi dan siklus pengumpulan sampah. Anda akan melihat grafik yang mirip dengan gambar berikut:
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:
Jenis akun System.String untuk alokasi terbanyak. 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: SensorMeasurement
jenisnya, dan jenisnya 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 class
jenis. 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 jalur panas. Namun, keputusan itu berarti sejumlah besar alokasi. Ubah jenis dari menjadi class
struct
.
Mengubah dari class
untuk struct
memperkenalkan beberapa kesalahan pengompilasi karena kode asli menggunakan null
pemeriksaan referensi di beberapa tempat. Yang pertama ada di DebounceMeasurement
kelas , dalam AddMeasurement
metode :
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 berisi DebounceMeasurement
array 50 pengukuran. Pembacaan untuk sensor dilaporkan sebagai rata-rata dari 50 pengukuran terakhir. Itu mengurangi kebisingan dalam pembacaan. Sebelum pembacaan 50 lengkap diambil, nilai-nilai ini adalah null
. Kode memeriksa referensi untuk null
melaporkan rata-rata yang benar pada startup sistem. Setelah mengubah jenis menjadi SensorMeasurement
struct, Anda harus menggunakan pengujian yang berbeda. Jenisnya SensorMeasurement
mencakup string
untuk pengidentifikasi ruangan, sehingga Anda dapat menggunakan pengujian tersebut sebagai gantinya:
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 pemula, variabel lokal untuk SensorMeasurement
adalah referensi nullable:
SensorMeasurement? measure = default;
Sekarang setelah SensorMeasurement
adalah struct
bukan class
, yang nullable adalah jenis 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
jenis diteruskan oleh nilai, modifikasi yang dilakukan pada parameter metode tidak terlihat setelah metode kembali.
Penting
Mengubah jenis dari ke class
struct
dapat mengubah semantik program Anda. class
Ketika jenis diteruskan ke metode, setiap mutasi yang dibuat dalam metode dibuat ke argumen. struct
Ketika jenis diteruskan ke metode, dan mutasi yang dibuat dalam metode dibuat ke salinan argumen. Itu berarti metode apa pun yang memodifikasi argumennya berdasarkan desain harus diperbarui untuk menggunakan ref
pengubah pada jenis argumen apa pun yang telah Anda ubah dari menjadi class
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 memberlakukan readonly
sifat 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
saat Anda mengubah jenis dari ke class
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, ia melakukan pekerjaan ekstra menyalin SensorMeasurement
struktur setiap kali digunakan sebagai parameter atau nilai pengembalian. Struktur SensorMeasurement
berisi empat ganda, DateTime dan string
. Struktur itu terukur lebih besar dari 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);
}
Itu menyimpan satu operasi penyalinan. Parameter in
adalah referensi ke salinan yang sudah dibuat oleh pemanggil. Anda juga dapat menyimpan salinan dengan TakeMeasurement
metode dalam Room
jenis . Metode ini menggambarkan bagaimana pengkompilasi memberikan keamanan ketika Anda meneruskan argumen dengan ref
. Metode awal TakeMeasurement
dalam Room
jenis mengambil argumen dari 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 jenis class
. Membuat Room
objek melibatkan beberapa alokasi. Satu untuk dirinya Room
sendiri, dan satu untuk setiap anggota jenis 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.
DebounceMeasurement
Ubah jenis dari menjadi class
struct
. Itu memperkenalkan kesalahan CS8983: A 'struct' with field initializers must include an explicitly declared constructor
kompilator . 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 salah satu nilai struktur. Anda dapat menambahkan pengubah ke deklarasi metode tersebut readonly
. Jenisnya DebounceMeasurement
dapat diubah, jadi Anda harus berhati-hati bahwa modifikasi tidak memengaruhi salinan yang dibuang. Metode AddMeasurement
ini mengubah status objek. Ini dipanggil dari Room
kelas , dalam TakeMeasurements
metode . 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 readonly yang mengembalikan referensi baca-saja ke instans yang dimiliki oleh ruangan ini. Sekarang didukung oleh bidang yang dideklarasikan yang diinisialisasi ketika Room
objek dibuat. Setelah membuat perubahan ini, Anda akan memperbarui implementasi AddMeasurement
metode. Ini menggunakan bidang backing privat, debounce
, bukan properti Debounce
readonly . Dengan begitu, perubahan terjadi pada instans tunggal yang dibuat selama inisialisasi.
Teknik yang sama bekerja dengan Average
properti . Pertama, Anda memodifikasi AverageMeasurement
jenis dari ke class
struct
, dan menambahkan readonly
pengubah pada ToString
metode :
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
kelas mengikuti teknik yang sama dengan yang Anda gunakan untuk Debounce
properti . Properti Average
mengembalikan readonly ref
ke bidang privat untuk pengukuran rata-rata. Metode memodifikasi AddMeasurement
bidang internal.
private AverageMeasurement average = new();
public ref readonly AverageMeasurement Average { get { return ref average; } }
Hindari tinju
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.
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.
- Mengonversi kelas ke struct: Berkali-kali, jenis dapat dikonversi dari menjadi
class
struct
. Aplikasi Anda menggunakan ruang tumpukan alih-alih membuat alokasi tumpukan. - Mempertahankan semantik: Mengonversi ke
class
struct
dapat memengaruhi semantik untuk parameter dan mengembalikan nilai. Metode apa pun yang memodifikasi parameternya sekarang harus menandai parameter tersebut dengan pengubahref
. 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 pengubahref
. - 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 denganreadonly ref
untuk mengembalikan referensi yang tidak dapat dimodifikasi.
Dengan menggunakan teknik ini, Anda dapat meningkatkan performa di jalur panas kode Anda.