Blog
Mert Özen'in yazılım geliştirme, backend, frontend ve DevOps üzerine blog yazıları.
Programlama Dilleri Nasıl Evrilir: Lisp'ten C#'a Paradigmalar ve Runtime Async'e Uzanan Yol
Dil özellikleri çoğu zaman önce compiler'ın var olan yapılarla taklit ettiği bir hile olarak doğar, yaygınlaşınca runtime onu doğrudan destekler: generic'ler, lambda'lar ve .NET 11'in Runtime Async'i bunun örnekleri. Fortran ve Lisp'ten C#'a kısa bir soy ağacı, paradigmaların C#'ta bir arada kullanımı, static/dynamic ve strong/weak typing, C# ile TypeScript'in nominal ve structural farkı ve Runtime Async ile 13 satırdan 5 satıra inen canlı stack, gerçek çıktılarla.
Garbage Collection Nasıl Çalışır: Mark-Sweep-Compact, Generational GC ve .NET'in Server GC'si
Tracing GC, root'lardan ulaşılabilen object'leri işaretleyip gerisini çöp sayar; .NET bu işi generation'lara bölüp write barrier ve card table ile gen0 GC'yi ucuz tutar. Bookstore filtre dilinin interpreter'ının beş saniyede 10 GB allocation ve 1.365 gen0 GC ürettiğini, derlenmiş delegate'in ise hiç GC tetiklemediğini dotnet-counters ve BenchmarkDotNet ile gösteriyor; LOH'u, Workstation, Server GC ve DATAS'ı, gerçek leak ile toplanmamış belleğin farkını ölçüyoruz.
AOT Compilation: ReadyToRun, Native AOT ve Startup ile Throughput Arasındaki Seçim
Native AOT, makine kodunu publish sırasında üretip startup'ı ve belleği belirgin şekilde azaltır; bedeli trimming, dynamic code yasağı, platforma özel build ve Dynamic PGO'nun yokluğudur. Aynı Bookstore API'sini JIT, ReadyToRun ve Native AOT ile publish edip startup, bellek ve throughput ölçüyor; filtre dilimizin AOT'de verdiği uyarıları, uyarı vermeden 500 dönen trimming hatasını ve Expression.Compile()'ın interpreter'a dönmesinin bedelini gösteriyoruz.
IL ve JIT Compiler: Tiered Compilation, Dynamic PGO, Inlining ve Escape Analysis
JIT bir method'u tek seferde değil kademeli derler: önce optimizasyonsuz Tier0, sonra sayaçlı bir kopya, en son toplanan profile göre optimize edilmiş Tier1. Bookstore koduyla IL'in stack tabanlı yapısını, OSR'ı, Dynamic PGO'nun interface çağrısını tip kontrolüyle doğrudan koda çevirmesini, bounds check eliminasyonunu ve .NET 10'un escape analysis'iyle 40 byte'lık allocation'ın sıfıra inmesini DOTNET_JitDisasm çıktılarıyla gösteriyor, V8'in hidden class ve deoptimization'ıyla karşılaştırıyoruz.
Semantic Analysis ve Code Generation: Type Checking, Symbol Table ve AST'den Expression Tree'ye
Semantic analysis, parse edilmiş ağaçtaki isimleri symbol table'a bağlayıp tipleri kontrol eder; price = "abc" gibi bir hata böylece filtre hiçbir kitaba uygulanmadan yakalanır. Bookstore filtre dili için binder, implicit conversion ve constant folding yazıyor, doğrulanmış ağacı Expression<Func<Book,bool>>'a derleyip hem bellekte çalıştırıyor hem EF Core'un parametreli SQL'e çevirdiğini ToQueryString ile gösteriyoruz; uzunluk sınırının neden gerektiğini ve Roslyn'deki karşılığını da görüyoruz.
Parser ve AST: Recursive Descent, Operator Precedence ve Pratt Parsing
Parser, token dizisini precedence ve associativity kurallarına göre tek bir AST'ye çevirir; Pratt parsing bu kuralları method zinciri yerine bir binding power tablosunda tutar. Bookstore filtre dili için grammar'dan recursive descent parser'ı türetiyor, aynı dili Pratt parser ile yazıp iki parser'ın aynı ağacı ürettiğini test ediyor, panic mode ile birden çok hata raporluyor ve AST'yi tree-walking interpreter ile çalıştırıyoruz.
Lexer Yazmak: Karakterlerden Token'lara, C# ile Bookstore Filtre Dili
Lexer, karakter dizisini token'lara bölen ve her token'ın satırını ve sütununu bilen bir state machine'dir. Bookstore filtre dili için ReadOnlySpan<char> üzerinde tek geçişte çalışan bir C# lexer'ı yazıyoruz: maximal munch, Türkçe culture'da bozulmayan keyword eşleşmesi, InvariantCulture ile sayı okuma, surrogate pair'e dayanıklı hata kurtarma ve anlaşılır hata mesajları. Regex ile lexer'ın nerede kırıldığını ve Roslyn'in boşlukları neden trivia olarak sakladığını gerçek çıktılarla gösteriyoruz.
Kod Nasıl Çalışır Hâle Gelir: Compiler, Interpreter, JIT ve AOT'nin Haritası
Derlenen ya da yorumlanan dil diye bir ayrım yok; aynı aşamaların hangisinin build'de, hangisinin çalışma anında yapıldığı implementation'ın seçimi. 1.500 kez çağrılan bir C# method'u JIT tarafından yalnız üç kez derleniyor, TypeScript tipleri build'de kontrol edilip siliniyor, V8 ise kodu önce yorumlayıp sonra Maglev ve TurboFan ile derliyor. Yazı compiler pipeline'ını gerçek çıktılarla gösteriyor ve seri boyunca yazılacak Bookstore filtre dilinin grammar'ını çıkarıyor.
Concurrency Modelleri ve Concurrent Kodu Doğrulamak: Shared Memory, Actor, CSP ve Model Checking
Concurrency modeli durumun sahibini ve haberleşme şeklini belirler: shared memory'de herkes kilitle sırayla dokunur, actor ve CSP'de durumun tek sahibi vardır ve haberleşme mesajladır. Bookstore stok rezervasyonunu lock, actor ve channel pipeline ile kuruyor, actor'ün içi hatasızken oku-sonra-yaz protokolünün 1.000 stoktan 4.000 kitap sattırdığını gösteriyor ve aynı check-then-act hatasını TLA+ ile model check edip TLC'nin karşı örneğini okuyoruz.
Lock-Free ve Wait-Free: CAS Döngüsü, ABA Problemi ve Linearizability
Lock-free bir algoritma rekabet altında bile en az bir thread'in ilerlemesini garanti eder, wait-free ise her thread'in. CAS döngüsüyle bir sayaç ve Treiber stack yazıyor, node havuzlamanın ABA problemini geri getirip 8 milyon id'nin yüz binlercesini kaybettirdiğini, küçük bir checker'ın bozuk stack'te linearizable olmayan geçmişler bulduğunu ve 8 thread'de CAS döngüsünün lock'tan 16 kat yavaş kaldığını ARM64'te ölçüyoruz.
.NET Memory Model: Happens-Before, Acquire/Release, volatile ve Güvenli Object Publication
.NET memory model'inde sıradan erişimler yeniden sıralanabilir; Volatile.Read acquire, Volatile.Write release, Interlocked ve lock ise sıra garantisi veren noktalardır. Release build'de volatile olmayan bir stop flag döngüsünün JIT yüzünden hiç durmadığını, bool bayrakla yazılmış double-checked locking'in ARM64'te 20 milyon çağrıda 36 kez null döndürdüğünü gösteriyor, referans kontrolünün neden doğru olduğunu spec'ten çıkarıyor ve Java, C++ ve Go ile karşılaştırıyoruz.
Hardware Memory Model: Reordering, Store Buffer, Cache Coherence ve Sequential Consistency
Modern CPU'lar sequential consistency vermez: store buffer bir okumanın kendinden önceki yazıyı geçmesine izin verir ve ARM64 bundan da fazlasını yeniden sıralar. ARM64'te klasik store buffering testinde 10 milyon turun 5,3 milyonunda iki thread'in de 0 okuduğunu, message passing testinde bayrağın tutardan önce göründüğünü ölçüyor, cache coherence'ın neden bunu engellemediğini ve x64'ün hangi sonuçları yasakladığını Intel kılavuzundan gösteriyoruz.