Episode ini membahas performa dan diagnostik .NET: profiling dengan dotnet-trace dan dotnet-counters, analisis memori dan garbage collection, optimasi alokasi dengan Span, Memory, dan stackalloc, serta JIT optimization dan tiered compilation.

Kode yang berjalan lambat bukan selalu salah logika — sering kali masalahnya adalah alokasi memori yang boros, GC yang bekerja terlalu keras, atau query yang memuat data terlalu banyak. Tapi tebakan tidak cukup: performa harus diukur.
Toolchain .NET menyediakan alat diagnostik kelas produksi yang bisa dipasang dan dijalankan dengan satu perintah. Dimulai dari pengukuran yang benar, optimasi baru layak dilakukan.
Episode 15 ini mengajarkan profiling dengan dotnet-trace dan dotnet-counters, diagnosis memori, optimasi dengan tipe baru seperti Span, serta pemahaman JIT dan tiered compilation.
dotnet-counters memantau metrik runtime secara real-time — alokasi memori, GC, dan utilisasi CPU:
dotnet tool install --global dotnet-counters
dotnet counters monitor -n NamaAplikasi --counters System.RuntimePerintah dotnet counters monitor menampilkan metrik seperti gen-0-gc-count, cpu-usage, dan alloc-rate. Jika alokasi memori tinggi terus-menerus, inilah petunjuk pertama untuk menyelidiki alokasi yang tidak perlu.
Untuk melihat ke mana waktu benar-benar pergi, gunakan dotnet-trace yang menghasilkan file trace berisi call stack dan durasi tiap method:
dotnet tool install --global dotnet-trace
dotnet trace collect -p <pid> -o trace.nettrace
dotnet trace convert trace.nettrace --format speedscopeHasil konversi --format speedscope bisa dibuka di editor seperti VS Code untuk membaca flamegraph — visualisasi durasi tiap pemanggilan. Dari sini kalian tahu method mana yang harus dioptimasi, bukan menebak.
Selain alat eksternal, .NET punya API untuk mengukur alokasi dari dalam kode:
static long HitungAlokasi(Func<int> kerja)
{
var sebelum = GC.GetAllocatedBytesForCurrentThread();
var hasil = kerja();
var sesudah = GC.GetAllocatedBytesForCurrentThread();
return sesudah - sebelum;
}
var alokasi = HitungAlokasi(() => ProsesData());
Console.WriteLine($"Alokasi: {alokasi} byte");GC.GetAllocatedBytesForCurrentThread menghitung byte yang dialokasikan oleh kode di thread tersebut. Mengetahui angka alokasi per operasi membantu kalian mengevaluasi apakah optimasi benar-benar berdampak.
Pola yang menghasilkan banyak objek kecil sementara — seperti menyusun string berulang kali — membuat GC bekerja keras. Gunakan StringBuilder untuk penggabungan string di dalam loop, dan pilih ArrayPool untuk buffer yang sering dipakai.
Span<T> memungkinkan mengakses slice dari array atau memori lain tanpa mengalokasikan objek baru:
static int JumlahDigit(Span<char> teks)
{
var total = 0;
foreach (var c in teks)
{
if (char.IsDigit(c)) total++;
}
return total;
}
var data = "Order#12345".AsSpan();
Console.WriteLine($"Digit: {JumlahDigit(data)}");data.AsSpan() membuat view ringan tanpa menyalin string. Operasi parsing yang memakai banyak substring kini bisa dilakukan tanpa satu pun alokasi.
Untuk buffer berukuran kecil dan tetap, stackalloc mengalokasikannya di stack — tanpa menyentuh heap sama sekali:
Span<byte> buffer = stackalloc byte[256];
buffer[0] = 0x01;
Console.WriteLine($"Byte pertama: {buffer[0]}");stackalloc byte[256] dialokasikan di stack dan otomatis dibuang saat method selesai. Gunakan untuk buffer kecil di jalur kode yang sangat kritis — jangan untuk ukuran besar atau variabel.
Runtime .NET memakai tiered compilation dalam dua tingkatan: metode di-compile cepat (tier 0) saat pertama dipanggil, lalu di-recompile dengan optimasi penuh (tier 1) jika sering digunakan. Ini memberi startup cepat sekaligus performa puncak untuk kode panas.
Untuk path yang benar-benar kritis, kalian bisa memberi tahu JIT dengan AggressiveOptimization pada method:
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
static int HitungKritis(int a, int b)
{
return a * b + a / b;
}AggressiveOptimization mendorong JIT meng-compile method langsung ke tier yang lebih optimal. Gunakan hemat — hanya untuk method yang terbukti sebagai hotspot dari hasil profiling, bukan untuk semua method.
Inti yang harus dibawa pulang:
GC.GetAllocatedBytesForCurrentThread mengukur dampak optimasi.Span<T> dan stackalloc menghilangkan alokasi di jalur kritis.Di episode 16 selanjutnya kita menyusun proyek berskala besar: architecture dan design patterns — clean architecture dan layered architecture, dependency injection, CQRS dan mediator pattern, serta DDD fundamentals dengan modular solution organization.