Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Penting
Teknik yang dijelaskan di bagian ini meningkatkan performa saat diterapkan ke jalur panas dalam kode Anda. Jalur panas adalah bagian basis kode Anda yang sering dijalankan dan berulang kali dalam operasi normal. Menerapkan teknik ini ke kode yang tidak sering dijalankan akan berdampak minimal. Sebelum membuat perubahan apa pun untuk meningkatkan performa, sangat penting untuk mengukur garis besar. Kemudian, analisis garis besar tersebut untuk menentukan di mana penyempitan memori terjadi. Anda dapat mempelajari tentang banyak alat lintas platform untuk mengukur performa aplikasi Anda di bagian diagnostik dan instrumentasi. Anda dapat mempraktikkan sesi pembuatan profil dalam tutorial untuk Mengukur penggunaan memori dalam dokumentasi Visual Studio.
Setelah Anda mengukur penggunaan memori dan telah menentukan bahwa Anda dapat mengurangi alokasi, gunakan teknik di bagian ini untuk mengurangi alokasi. Setelah setiap perubahan berturut-turut, ukur penggunaan memori lagi. Pastikan setiap perubahan memiliki dampak positif pada penggunaan memori di aplikasi Anda.
Pekerjaan terkait kinerja di .NET sering kali berarti mengoptimalkan dengan menghapus alokasi dari kode Anda. Setiap blok memori yang Anda alokasikan pada akhirnya harus dibebaskan. Lebih sedikit alokasi mengurangi waktu yang dihabiskan dalam pengumpulan sampah. Ini memungkinkan waktu eksekusi yang lebih dapat diprediksi dengan menghapus koleksi sampah dari jalur kode tertentu.
Taktik umum untuk mengurangi alokasi adalah mengubah struktur data penting dari class
jenis ke struct
jenis. Perubahan ini berdampak pada semantik penggunaan jenis tersebut. Parameter dan nilai balik sekarang dikirimkan sebagai nilai alih-alih sebagai referensi. Biaya penyalinan nilai dapat diabaikan jika jenisnya kecil, tiga kata atau kurang (mempertimbangkan satu kata dengan ukuran alami satu bilangan bulat). Ini dapat diukur dan dapat memiliki dampak performa nyata untuk jenis yang lebih besar. Untuk melawan dampak penyalinan, pengembang dapat melewatkan jenis-jenis ini dengan ref
yang dimaksudkan untuk mendapatkan kembali semantik yang diinginkan.
Fitur C# ref
memberi Anda kemampuan untuk mengekspresikan semantik yang diinginkan untuk struct
jenis tanpa berdampak negatif pada kegunaan keseluruhannya. Sebelum peningkatan ini, pengembang perlu menggunakan unsafe
konstruksi dengan pointer dan memori mentah untuk mencapai pengaruh kinerja yang sama. Pengkompilasi menghasilkan kode yang aman untuk fitur terkait baru ref
.
Kode aman yang dapat diverifikasi berarti kompilator mendeteksi kemungkinan buffer overruns atau mengakses memori yang tidak dialokasikan atau dibebaskan. Pengkompilasi mendeteksi dan mencegah beberapa kesalahan.
Meneruskan dan mengembalikan berdasarkan referensi
Variabel di C# menyimpan nilai. Dalam tipe struct
, nilai adalah isi dari sebuah instance tipe tersebut. Dalam tipe class
, nilai adalah referensi ke blok memori yang menyimpan instance tipe. Menambahkan pengubah ref
berarti variabel menyimpan referensi ke nilai . Dalam jenis struct
, acuan menunjuk ke tempat penyimpanan yang berisi nilai. Pada jenis class
, referensi menunjuk ke penyimpanan yang berisi referensi ke blok memori.
Dalam C#, parameter untuk metode dikirim sebagai nilai, dan nilai yang dikembalikan dikembalikan sebagai nilai. Nilai argumen diteruskan ke metode . Nilai argumen pengembalian adalah nilai yang dikembalikan.
Pengubah ref
, in
, ref readonly
, atau out
menunjukkan bahwa argumen dikirimkan melalui referensi.
Referensi ke lokasi penyimpanan diteruskan ke metode . Menambahkan ref
ke tanda tangan metode berarti bahwa nilai pengembalian dikembalikan melalui referensi.
Referensi ke lokasi penyimpanan adalah nilai pengembalian.
Anda juga dapat menggunakan penetapan ref untuk memiliki variabel yang merujuk ke variabel lain. Penugasan yang tipikal menyalin nilai dari sisi kanan ke variabel pada sisi kiri penugasan.
Penetapan ref menyalin lokasi memori variabel di sisi kanan ke variabel di sisi kiri. Sekarang ref
mengacu pada variabel asli:
int anInteger = 42; // assignment.
ref int location = ref anInteger; // ref assignment.
ref int sameLocation = ref location; // ref assignment
Console.WriteLine(location); // output: 42
sameLocation = 19; // assignment
Console.WriteLine(anInteger); // output: 19
Saat menetapkan variabel, Anda mengubah nilainya. Saat Ref menetapkan variabel, Anda mengubah apa yang dirujuknya.
Anda dapat bekerja langsung dengan penyimpanan untuk nilai menggunakan ref
variabel, melewati referensi, dan penetapan ref. Aturan cakupan yang diberlakukan oleh pengkompilasi memastikan keamanan saat bekerja langsung dengan penyimpanan.
Pengubah ref readonly
dan in
menunjukkan bahwa argumen harus diteruskan oleh referensi dan tidak dapat ditetapkan kembali dalam metode . Perbedaannya adalah yang ref readonly
menunjukkan bahwa metode menggunakan parameter sebagai variabel. Metode ini mungkin menangkap parameter, atau mungkin mengembalikan parameter dengan referensi baca-saja. Dalam kasus tersebut, Anda harus menggunakan pengubah ref readonly
. Jika tidak, pengubah in
menawarkan lebih banyak fleksibilitas. Anda tidak perlu menambahkan pengubah in
ke argumen untuk in
parameter, sehingga Anda dapat memperbarui tanda tangan API yang ada dengan aman menggunakan pengubah in
. Pengkompilasi mengeluarkan peringatan jika Anda tidak menambahkan ref
pengubah atau in
ke argumen untuk ref readonly
parameter.
Referensi konteks yang aman
C# menyertakan aturan untuk ref
ekspresi untuk memastikan bahwa ref
ekspresi tidak dapat diakses di mana penyimpanan yang dirujuknya tidak lagi valid. Pertimbangkan contoh berikut:
public ref int CantEscape()
{
int index = 42;
return ref index; // Error: index's ref safe context is the body of CantEscape
}
Pengkompilasi melaporkan kesalahan karena Anda tidak dapat mengembalikan referensi ke variabel lokal dari metode . Pemanggil tidak dapat mengakses penyimpanan yang dirujuk.
Konteks aman ref menentukan cakupan di mana ref
ekspresi aman untuk diakses atau dimodifikasi. Tabel berikut mencantumkan konteks aman ref untuk jenis variabel.
ref
bidang tidak dapat dideklarasikan dalam class
atau non-ref struct
, sehingga baris tersebut tidak ada dalam tabel:
Deklarasi | referensi konteks aman |
---|---|
lokal tidak terujuk | blok tempat variabel lokal dideklarasikan |
parameter non-ref | metode saat ini |
ref , ref readonly , in parameter |
metode panggilan |
parameter out |
metode saat ini |
class bidang |
metode panggilan |
bidang non-referensi struct |
metode saat ini |
ref bidang dari ref struct |
metode panggilan |
Variabel dapat ref
dikembalikan jika konteks aman ref-nya adalah metode panggilan. Jika konteks aman referensinya adalah metode saat ini atau blok, ref
pengembalian tidak diperbolehkan. Cuplikan berikut menunjukkan dua contoh. Bidang anggota dapat diakses dari cakupan metode yang memanggil, sehingga konteks aman ref dari bidang kelas atau struct adalah metode yang memanggil.
Konteks aman ref untuk parameter dengan ref
, atau in
pengubah adalah seluruh metode. Keduanya dapat ref
dikembalikan dari metode anggota:
private int anIndex;
public ref int RetrieveIndexRef()
{
return ref anIndex;
}
public ref int RefMin(ref int left, ref int right)
{
if (left < right)
return ref left;
else
return ref right;
}
Nota
Ketika pengubah ref readonly
atau in
diterapkan ke parameter, parameter tersebut dapat dikembalikan oleh ref readonly
, bukan ref
.
Pengkompilasi memastikan bahwa referensi tidak dapat lolos dari konteks aman ref-nya. Anda dapat menggunakan ref
parameter, ref return
, dan ref
variabel lokal dengan aman karena pengompilasi mendeteksi apakah Anda tidak sengaja menulis kode di mana ref
ekspresi dapat diakses saat penyimpanannya tidak valid.
Konteks aman dan struktur ref
ref struct
jenis memerlukan lebih banyak aturan untuk memastikan mereka dapat digunakan dengan aman. Jenis ref struct
dapat menyertakan ref
bidang. Itu membutuhkan pengenalan konteks yang aman. Untuk sebagian besar jenis, konteks aman adalah metode panggilan. Dengan kata lain, nilai yang bukan ref struct
dapat selalu dikembalikan dari metode.
Secara informal, cakupan yang aman untuk adalah konteks di mana semua field-field ref struct
dapat diakses. Dengan kata lain, ini adalah persimpangan konteks aman ref dari semua bidangnya ref
. Metode berikut mengembalikan ReadOnlySpan<char>
ke bidang milik anggota, sehingga konteks amannya ada pada metode tersebut:
private string longMessage = "This is a long message";
public ReadOnlySpan<char> Safe()
{
var span = longMessage.AsSpan();
return span;
}
Sebaliknya, kode berikut menghasilkan kesalahan karena anggota ref field
dari Span<int>
mengacu pada array bilangan bulat yang dialokasikan di stack. Ini tidak dapat menghindari metode tersebut.
public Span<int> M()
{
int length = 3;
Span<int> numbers = stackalloc int[length];
for (var i = 0; i < length; i++)
{
numbers[i] = i;
}
return numbers; // Error! numbers can't escape this method.
}
Menyatukan jenis memori
Pengenalan System.Span<T> dan System.Memory<T> menyediakan model terpadu untuk bekerja dengan memori.
System.ReadOnlySpan<T> dan System.ReadOnlyMemory<T> menyediakan versi readonly untuk mengakses memori. Semuanya memberikan abstraksi atas blok memori yang menyimpan array elemen serupa. Perbedaannya adalah bahwa Span<T>
dan ReadOnlySpan<T>
merupakan ref struct
jenis sedangkan Memory<T>
dan ReadOnlyMemory<T>
merupakan struct
jenis. Rentang berisi ref field
. Oleh karena itu, contoh rentang tidak dapat meninggalkan konteks yang aman.
Konteks aman dari ref struct
adalah konteks aman ref dari ref field
. Implementasi Memory<T>
dan ReadOnlyMemory<T>
hapus pembatasan ini. Anda menggunakan jenis ini untuk langsung mengakses buffer memori.
Meningkatkan performa dengan keamanan ref
Menggunakan fitur-fitur ini untuk meningkatkan performa melibatkan tugas-tugas ini:
-
Hindari alokasi: Saat Anda mengubah jenis dari
class
menjadistruct
, Anda mengubah cara penyimpanannya. Variabel lokal disimpan pada tumpukan. Anggota disimpan sebaris saat objek kontainer dialokasikan. Perubahan ini berarti lebih sedikit alokasi dan yang mengurangi pekerjaan yang dilakukan pengumpul sampah. Ini mungkin juga mengurangi tekanan memori sehingga pengumpul sampah berjalan lebih jarang. -
Mempertahankan semantik referensi: Mengubah tipe dari
class
menjadistruct
mengubah semantik saat meneruskan variabel ke metode. Kode yang memodifikasi status parameternya perlu dimodifikasi. Sekarang setelah parameter adalahstruct
, metode memodifikasi salinan objek asli. Anda dapat memulihkan semantik asli dengan meneruskan parameter tersebut sebagairef
parameter. Setelah perubahan itu, metode tersebut memodifikasistruct
yang asli lagi. -
Hindari menyalin data: Menyalin jenis yang lebih besar
struct
dapat memengaruhi performa di beberapa jalur kode. Anda juga dapat menambahkan pengubahref
untuk meneruskan struktur data yang lebih besar ke metode berdasarkan referensi, bukan berdasarkan nilai. -
Batasi modifikasi: Saat
struct
tipe dikirimkan melalui referensi, metode yang dipanggil dapat mengubah status struktur. Anda dapat mengganti pengubahref
dengan pengubahref readonly
atauin
untuk menunjukkan bahwa argumen tidak dapat dimodifikasi. Lebih sukaref readonly
ketika metode mengambil parameter atau mengembalikannya dengan referensi baca-saja. Anda juga dapat membuat tipereadonly struct
ataustruct
dengan anggotareadonly
untuk memberikan kontrol lebih lanjut atas anggota daristruct
yang bisa dimodifikasi. -
Memanipulasi memori secara langsung: Beberapa algoritma paling efisien saat memperlakukan struktur data sebagai blok memori yang berisi urutan elemen. Jenis
Span
danMemory
menyediakan akses aman ke blok memori.
Tidak ada teknik ini yang memerlukan unsafe
kode. Digunakan dengan bijak, Anda bisa mendapatkan karakteristik performa dari kode aman yang sebelumnya hanya dimungkinkan dengan menggunakan teknik yang tidak aman. Anda dapat mencoba teknik sendiri dalam tutorial tentang mengurangi alokasi memori.