GOGOGOLLC
Blog'a dön
EngineeringMay 19, 20269 min read

Kod çalıştığında gerçekte ne oluyor.

Kaynak kod makinenin yürüttüğü şeyle aynı değil. Yazdığın her satır beş çeviri katmanından iniyor, transistörde elektronlarda bitiyor. İşte merdiven, katman katman, yazabileceğim en somut dilde.

Atakan Özalan

Atakan Özalan

Kurucu ortak & mühendislik lideri, GOGOGO LLC

Kod çalıştığında gerçekte ne oluyor.

Bu soruyu beklediğimden daha sık alıyorum, genellikle günlük kod yazan ama run'a bastıktan sonra ne olduğuna yakınlaşmamış insanlardan. Dürüst cevap beş katman alıyor. Her katman altındakini gizliyor. Çoğunlukla bu bir naziklik ama nasıl çalıştığını bilmek üst katmanı nasıl yazdığını değiştiriyor. Bu, yazabildiğim en somut dilde merdiven.

Katman 1 — Kaynak kod

Üst katman senin yazdığın. Python'da if x > 5: print('big') veya JavaScript'te console.log("hi"). Birkaç noktalama ile İngilizce gibi görünüyor. Makinenin çalıştırdığı şey değil. Bu, insanların okunabilir olması için tasarladığı bir notasyonda, ne istediğinin bir tarifi. CPU asla kaynak kodunu okumadı ve okumayacak.

Bu katman neyde iyi: insanların düşünme şekline uymakta. Döngüler, değişkenler, adlandırılmış fonksiyonlar — bunlar zaten sahip olduğumuz zihinsel modellere karşılık geliyor. Derleyici / yorumlayıcının işi o uyumu makinenin kullanabileceği bir şeye temiz şekilde yıkmak.

Katman 2 — Bytecode / ara temsil

Modern dillerin çoğu kaynağı doğrudan makine koduna derlemez. Önce bir ara temsile çevirirler — bytecode, IR, daha düz AST. Python'ın .pyc dosyaları bu. Java'nın .class dosyaları bu. JavaScript motorlarının Ignition adlı gizli bir IR'si var. C bile makine koduna inmeden önce LLVM'in IR'sinden geçiyor.

Bytecode düşük seviyeli bir sanal komut seti — kaynağındakinden daha basit işlemler ama henüz gerçek bir CPU'ya özgü değil. LOAD_FAST, CALL_FUNCTION, BINARY_ADD, JUMP_IF_FALSE. Düzineler kadar farklı işlem, değişken adları kalmadı, boşluk yok, yorum yok. Bir Python yorumlayıcısının gerçekten üzerinde yürüdüğü şey bu.

Bu katmanın neden var olduğu: taşınabilir. Aynı bytecode doğru sanal makinenin kurulu olduğu her makinede çalışıyor. Derleyici/yorumlayıcının tek bir işi var — kaynağı bytecode'a çevirmek — ve VM gerisini yapıyor.

Katman 3 — Makine kodu

Bytecode taşınabilir ama yavaş, çünkü her bytecode komutu genellikle birden çok gerçek CPU komutuna eşleniyor. Daha hızlı gitmek için VM (veya bir JIT derleyici — Just-In-Time) sıcak bytecode yollarını makine koduna çeviriyor — özel CPU mimarinin anladığı ikili komutlar. x86-64'te MOV, ADD, CMP, JMP; telefonundaki veya M-serisi Mac'indeki ARM64'te eşdeğerleri.

Makine kodu CPU tarafından bellekten getirilen ve yürütülen şey. Bayt akışı — assembler mnemonic insan-okunabilir bir versiyon ama CPU 48 89 e5 gibi bir şey görüyor (x86-64'te mov rbp, rsp). CPU bunun Python script'inden geldiğine dair hiçbir fikri yok. Sadece baytları yürütüyor.

Katman 4 — CPU döngüleri

Makine kodu komutları anında yürütülmüyor. Modern bir CPU saniyede yaklaşık 3-4 milyar döngüde çalışıyor. Her komut 1 döngü (basit bir ADD) ile ~100+ döngü (RAM'den getirmek zorunda olan bir cache miss) arasında alıyor. CPU'nun pipeline'ları var — birden çok komut aynı anda uçuşta, fetch / decode / execute / write-back'in farklı aşamalarında. Sıra-dışı yürütme yapıyor — bağımlılıklar izin verdiğinde komutlar throughput için yeniden sıralanıyor. Dal tahmini yapıyor — bir if ifadesinin hangi yöne gideceğini tahmin ediyor ve spekülatif olarak yürütmeye başlıyor, yanlışsa geri alıyor.

Neredeyse hiçbir yazılım mühendisi günlük olarak bu katmanı düşünmüyor. Ama kodunun hızlı veya yavaş olmasının nedeni bu. Dallar öngörülebilirse pipeline dolu kalıyor. Bellek lokalitesi önemli çünkü cache miss'leri register hit'inin 100×'ine mal oluyor. SIMD vektör komutları veri düzeni doğruysa 4-8 sayıyı paralel işliyor. Birisi cache-friendly kod yazmanı söylediğinde, bahsettiği katman bu.

Katman 5 — Fizik

CPU döngüsünün altında gerçek fiziksel olay var. Bir CPU register'ı küçük bir bistable devre seti — birkaç milyar transistörden yapılmış, çipte. Bir transistör bir anahtar — kapısındaki voltaja bağlı olarak akım akıyor veya akmıyor. Makine-kodu seviyesinde ADD, ikili bir adder olarak düzenlenmiş bir anahtar ağında elektronların hareket etmesine, toplamı temsil eden yeni bir desene yerleşmesine karşılık geliyor. Her transistör nanosaniyenin altında bir sürede değişiyor. Saat sinyali — çipin kalp atışı — saniyede milyarlarca bu değişimi koordine ediyor.

Transistörlerin altında kuantum mekaniği var. Geçit oksitlerinden elektron tünelleme, bir transistörün ne kadar küçük olabileceğini sınırlayan şey. Isı dağıtma — Landauer ilkesi bir bit bilgiyi silmenin minimum termodinamik maliyeti olduğunu söylüyor — çipin ne kadar hızlı çalışabileceğini sınırlayan şey. 3nm prosesindeki modern çipler bu fiziksel tabanlara değmeye başlıyor, bu yüzden CPU saat hızları 2010'da yaklaşık 5 GHz'de plato yaptı ve ilerleme artık paralelizmden geliyor, ham frekanstan değil.

Merdivenin neden önemli olduğu

Bunun hiçbirini bilmeden çalışan yazılım yazabilirsin. On milyonlarca mühendis her gün yapıyor. Ama en güvendiğim mühendisler ihtiyaç duyduklarında merdivenden aşağı inebiliyor. Tuhaf bir performans bug'ı mı? Katman 4'e in — cache deseni ne, dal yanlış-tahmin oranı ne. Bytecode-seviyesi bir soru mu? Katman 2. Tuhaf bir floating-point sonucu mu? Katman 5 — IEEE 754, yuvarlama modları, denormaller. Merdiven egzotik bilgi değil; üst katmanın olduğu gibi davranmasını sağlayan substrat.

GOGOGO LLC'de zamanımın çoğunu Katman 1'de, TypeScript ve Python ile geçiriyorum. Çoklu-ajan runtime baştan sona Katman 1 kodu. Ama zihinsel modelimle eşleşmeyen bir çağrı-başına maliyet sayısı gördüğümde her zaman Katman 4'ü düşünüyorum. Birisi ajan latency tabanımızın model ~5ms'de döndüğünde bile neden ~30ms olduğunu sorduğunda Katman 5'i düşünüyorum (cevap: ağ round-trip fizik; fiber boyunca ışık hızı gerçek bir kısıt).

Kod tariftir. CPU fiziktir. Aralarındaki her soyutlama katmanı, üstteki bir şey tuhaf davrandığında ödeyeceğin bir borçtur. Merdiveni bilmek tahmin etmek ile akıl yürütmek arasındaki fark.

Daha genç bir mühendise söyleyeceklerim

Sonsuza kadar Katman 1'de kalma. İyi bir mühendis olmak için Assembly yazmana gerek yok — on yıldan fazla süredir gönüllü olarak x86 yazmadım — ama on dakika okuyabilmen ve ne olduğunu tanımalısın. Bytecode için de aynı. Bir transistörün temel fiziği için de aynı. Bu katmanların her biri sonunda seni bir hata ayıklama haftası kurtaracak. Yatırım küçük. Birikim büyük.

Katmanlardan herhangi birinde daha aşağı yürümek istersen — pipeline'lar, IR'ler, cache hiyerarşileri, CPU prefetcher'ların I-Ching'i — ulaşmak kolay. atakanozalan.com.

Bunu işin için ister misin?

Önce hangi iş akışını kuracağını anlat. Sana 4 fazlı bir plan ve uygun ajanlarla geri döneriz.