Saya Melakukan Hashing Setiap Kata Dua Kali Hanya untuk Menghitungnya Sekali
Saya melakukan profiling pada sebuah log parser minggu lalu. Program tersebut menghitung kode kesalahan dalam log harian. Logika intinya hanyalah pembaruan dictionary yang sederhana.
Kebanyakan pengembang menulisnya seperti ini:
if (counts.TryGetValue(code, out int c))
counts[code] = c + 1;
else
counts[code] = 1;
Kode ini melakukan hashing pada key sebanyak dua kali. TryGetValue menemukan entri dengan menghitung hash dan menelusuri bucket. Kemudian, indexer melakukan pekerjaan yang persis sama lagi untuk memperbarui nilainya. Anda membuang siklus CPU pada key dan hash yang sama dua kali untuk setiap token.
Anda dapat melewati perjalanan kedua tersebut. Gunakan CollectionsMarshal.GetValueRefOrAddDefault.
ref int slot = ref CollectionsMarshal.GetValueRefOrAddDefault(counts, code, out _);
slot++;
Metode ini menemukan atau membuat slot dalam satu langkah. Metode ini memberi Anda referensi langsung ke penyimpanannya (storage). Anda hanya melakukan satu kali hashing dan satu kali penelusuran bucket. Kemudian, Anda mengubah nilainya secara langsung (in place).
Saya menguji ini dengan 5 juta token.
Hasil: • TryGetValue + indexer: 160.0 ms • GetValueRefOrAddDefault: 95.0 ms
Versi satu-pencarian (one-lookup) ini 1.7x lebih cepat.
Yang terpenting, alokasi memori tetap identik. Trik ini tidak menghemat memori. Ini hanya menghemat CPU. Jika kode Anda lambat karena garbage collection, perubahan ini tidak akan berpengaruh apa-apa. Jika kode Anda lambat karena proses penghitungan yang berat, ini akan sangat membantu.
Gunakan ini saat loop Anda melakukan banyak pembaruan pada key yang sudah ada. Keuntungannya akan semakin besar seiring meningkatnya rasio pembaruan terhadap penyisipan (inserts).
Peringatan: Referensi tersebut menunjuk ke penyimpanan internal dictionary. Referensi ini hanya tetap valid hingga terjadi perubahan struktural berikutnya. Jangan menahan referensi tersebut jika Anda menambah atau menghapus key. Ambil ref-nya, ubah nilainya, dan lanjutkan.
Gunakan TryGetValue untuk sebagian besar tugas. Ini lebih mudah dibaca. Gunakan versi ref hanya jika loop dictionary Anda menjadi hambatan performa (performance bottleneck).
Sumber: https://dev.to/ssukhpinder/i-hashed-every-word-twice-to-count-it-once-1mg9
