.NET PROFILING NEDİR?
Production'da bir API yavaşladı. Sunucunun CPU kullanımı yarı yarıya boş, RAM şişmiş değil, exception log temiz. Buna rağmen p99 latency üç saniyeye dayanmış. Geliştirici klasik refleksle Stopwatch'lar yerleştirir, log seviyesini yükseltir, request başına süreleri yazdırır. Üç gün sonra elinde dağınık veri yığını var ama sorunun nerede olduğuna dair somut bir ipucu yok. Buradaki eksik şey ölçüm değil, gözlem: çalışan uygulamanın iç ritmini doğrudan görme yeteneği.
Profiling bu boşluğu doldurur. Kodun değil çalışan process'in fotoğrafını çeker — hangi metot ne kadar CPU yedi, hangi tip kaç defa allocate edildi, hangi thread'ler hangi lock'ta beklediyse hepsi tek bir görünümde toplanır. .NET ekosistemi son yıllarda bu konuda olgunlaştı; dotnet-trace ve dotnet-counters gibi resmi araçlardan JetBrains dotTrace ve PerfView gibi sektör standartlarına kadar geniş bir yelpaze var. Bu yazı .NET profiling'in ne olduğunu, hangi türleri kapsadığını ve tipik bir profil çalışmasının nasıl ilerlediğini sırasıyla ele alıyor.
.NET Profiling Yaklaşımı
Profiling, çalışan bir uygulamanın yürütme zamanı boyunca davranışını ölçen ve raporlayan bir disiplindir. Stopwatch ile bir bloğun süresini ölçmek tek-noktalı bir bilgidir; profiling ise sürecin tamamını mikro örnekleme veya event tabanlı izleme ile takip eder. .NET'te bu izleme büyük ölçüde ETW (Event Tracing for Windows) ve onun cross-platform muadili EventPipe üzerinden yürür. CLR (Common Language Runtime) çalışma sırasında binlerce event yayınlar — metot girişi, GC durumu, exception, thread bloklanması — profil aracı bu event'leri toplayıp anlamlı bir resme dönüştürür.
Sonuç şu olur: kodun hangi satırının ne kadar zaman aldığını tek tek ölçmek yerine, çalışan uygulamanın iskeletini sıcak yollar (hot paths), GC baskısı ve thread davranışı üzerinden bir bütün olarak görürsün.
Profiling ile Benchmarking Farkı
İkisi sık karıştırılır ama farklı sorulara cevap verir.
- Benchmarking: "İki uygulama arasında hangisi daha hızlı?" — kontrollü ortamda, izole edilmiş kod parçaları için tekrarlı ölçüm. BenchmarkDotNet bu işin standart aracıdır.
- Profiling: "Bu uygulama neden yavaş?" — gerçek workload altında, tüm uygulamanın iç davranışını izleme. Hangi metodun ne kadar süre tükettiğini, zamanın hangi yolda gittiğini gösterir.
Benchmark mikroskop, profiling röntgendir. Birini kullanırken diğerinin sorusunu beklersen yanılırsın.
.NET'te Profil Türleri
Tek tip profil yoktur. Aradığın sorunun cinsine göre farklı profil türleri devreye girer.
CPU profili
En yaygın profil türüdür. Çalışan process belirli bir frekansta (örn. her milisaniyede bir) örneklenir, o an çalışan call stack kaydedilir. Yeterince örnek biriktiğinde hangi metotların CPU zamanını yediği flame chart veya top-down/bottom-up görünümünde belirir. Sıcak yol burada ortaya çıkar.
Allocation profili
Her allocation (her new) izlenir. Hangi tipten kaç adet allocate edildiği, hangi metotta üretildiği, toplam allocate edilen byte miktarı raporlanır. Yüksek allocation = yüksek GC baskısı = duraklamalar. ASP.NET API'lerde request başına allocation sayısı tek başına kritik bir metriktir.
Memory snapshot ve leak analizi
Belirli bir anda heap'in tam fotoğrafını alır. Hangi nesneler bellekte duruyor, hangi referans zinciri onları GC'den koruyor, hangi tip beklenmedik biçimde birikmiş — hepsi snapshot üzerinden incelenir. Memory leak şüphesi olduğunda iki snapshot arasındaki delta ile sızıntının kaynağı bulunur.
Thread ve lock profili
Hangi thread ne yaptı, hangi lock'ta ne kadar bekledi, deadlock var mı, thread pool starvation yaşanıyor mu — bu sorulara cevap veren profil türü. Async-heavy uygulamalarda await üzerinde bekleme süreleri burada görünür.
Exception profili
Atılan ve yutulan exception sayısı şaşırtıcı performans sorunlarına yol açabilir. First-chance exception izleme, hot path'te sessizce atılıp yakalanan istisnaları ortaya çıkarır.
.NET için Profiling Araçları
.NET'te yerleşik ve üçüncü taraf araçların farklı güçlü yönleri var. Genellikle bir tek araç yetmez; iş ihtiyacına göre kombine edilir. Ekosisteme ilişkin diagnostics belgeleri tüm araç komut referanslarını ve canlı sorun giderme akışlarını içerir.
dotnet-counters
En hafif aracı. Komut satırından çalışır, çalışan process'in temel metriklerini (CPU, GC, exception count, thread pool, working set) canlı yayınlar. Production'da hızlı sağlık kontrolü için ideal.
dotnet-counters monitor --process-id 12345dotnet-trace
EventPipe üzerinden çalışan profilleyici. CPU, GC, allocation gibi event setlerini topla, .nettrace dosyasına yaz, sonra PerfView veya Visual Studio ile aç. Cross-platform; Linux production sunucusunda da çalışır.
dotnet-trace collect --process-id 12345 --profile cpu-samplingPerfView
Microsoft'un Windows tarafında veteran aracı. ETW event'lerini en derin seviyede analiz eder. Öğrenme eğrisi yüksek ama allocation tracking, GC analysis ve managed/unmanaged sınırı konularında hâlâ benzersiz.

Visual Studio Profiler
IDE entegre, geliştirici dostu. Performance Profiler menüsünden CPU usage, .NET object allocation tracking, database profile gibi modüller doğrudan IDE içinde çalıştırılır. Development sırasında ilk başvurulacak araç.
JetBrains dotTrace ve dotMemory
Sektörde yaygın kullanılan ticari ürünler. dotTrace CPU ve thread profile, dotMemory ise bellek snapshot ve leak analiz odaklı. Görselleştirme kalitesi ve kullanım kolaylığı bakımından yüksek puan alır.
Tipik Bir Profiling Akışı
Bir performans sorunu olduğunda izlenen klasik adımlar:
- Hipotez kur: Sorun CPU mu, allocation mı, lock contention mı? Semptomdan tahmin yap (CPU yüksek değil ama latency yüksekse muhtemelen I/O ya da lock bekleme).
- Doğru profil türünü seç: CPU semptomu için CPU profili, hafıza şişmesi için allocation veya snapshot.
- Gerçek workload altında topla: Boş bir endpoint'in profilini almak değerli değildir. Production benzeri yük altında veya load test sırasında kaydet.
- Hot path'i bul: Flame chart'ta en büyük blokları takip et. İlk %10 metot genellikle %80 zamanı tüketir (Pareto).
- Doğrulanmış hipotezle değiştir: Profil sana "bu metot %30 zaman yiyor" diyorsa, ondan önce kodu değiştirme. Sebebi anla, sonra düzelt.
- Tekrar profile et: Değişiklik sonrası aynı senaryoyu tekrar profile çıkar. İyileşme var mı, yan etki çıktı mı doğrula.
Bu akışı disiplinli uygulamak ve .NET test/performans araç setine pratik kazanmak için .NET test ve performans eğitiminden yararlanabilirsiniz; profiling, benchmarking ve test stratejilerini birlikte yapılandırır.
Profilde Sık Görülen Anti-Pattern'ler
Aynı tablolar farklı projelerde tekrar tekrar görünür. Birkaç klasik:
- String concat hot path'te:
+ile birleştirme her seferinde yeni allocation;StringBuilderveyastring.Createyerine geçer. - LINQ zinciri tight loop'ta: Her
.Where().Select().ToList()ara koleksiyon allocate eder. Hot path'te manuelfor+ struct enumerator daha hesaplı. - Async over sync: Senkron bir metodu
Task.Runile sahte async yapmak thread pool'u yorar. - Sık-sık-yeni-array: Buffer pooling kullanılmadığında her request başına büyük array allocate edilmesi;
ArrayPool<T>.Sharedbu yükü ciddi düşürür. - Exception kontrol akışı: Try/catch'in hot path'te kontrol akışı için kullanılması; her exception throw'u stack walk maliyetine sahiptir.
- Logger'ın eager evaluation'ı: Interpolated mesaj yazınca debug seviyesi kapalı olsa bile string allocate edilir. Yapısal logging veya
IsEnabledkontrolü gerekir.
Profil Sonuçlarını Yorumlama
Profil ham veri verir, yorum geliştiricinindir. Burada birkaç pratik kural işe yarar.
İlk bakışta self time (metodun kendi tükettiği zaman) ile total time (alt çağrılarla birlikte) arasındaki farkı oku. Self time yüksek ise metodun kendi kodu sorun. Total time yüksek ama self time düşükse problem alt çağrılardadır — derinleş.
Allocation profillerinde sayıdan çok tip dağılımına bak. Bin küçük string allocation ile bir büyük byte array farklı baskılar yaratır; küçük olanlar Gen 0'da kalır ucuzdur, büyük olanlar LOH (Large Object Heap) yığar ve pahalı toplama yapar.
Memory snapshot'larda retention path (nesneyi GC'den koruyan referans zinciri) en kritik bilgidir. "Şu nesneden 50 bin var" tek başına anlamsız; o 50 bin nesneyi kim tutuyor önemlidir. Statik bir liste, abone olunan ama unsubscribe edilmemiş event handler, kapanmamış cache — tipik suçlular.

Profiling'i Ne Zaman Yapmalı?
Geleneksel cevap: "Önce ölç, sonra optimize et." Bu doğru ama eksik. Profiling sadece sorun çıktığında değil, üç farklı momentte değerlidir. Birincisi, beklenmedik bir yavaşlama yaşandığında — semptom giderme. İkincisi, yeni bir feature'ı production'a vermeden önce baseline kayıt için — gelecekte değişikliklerin etkisini ölçebilmek adına. Üçüncüsü ise düzenli sağlık taraması olarak — production'da haftada bir kısa dotnet-counters bakışı, sorun büyümeden uyarı verir.
Profile etmeden optimize etmek tahmindir. .NET'in sunduğu araç seti bu tahminden çıkıp veri tabanlı karar almayı oldukça kolaylaştırıyor; öğrenme yatırımı kısa sürede geri döner.



