1 puan yazan GN⁺ 2025-06-08 | 1 yorum | WhatsApp'ta paylaş
  • Düşük seviyeli optimizasyon, Zig dilinde kolayca uygulanabilir
  • Derleyici çoğu durumda optimizasyonu iyi yapar; ancak bazen daha iyi performans için programcının niyetini açıkça iletmesi gerekir
  • Zig, derleme zamanı yürütme (comptime) özelliğiyle yüksek performanslı kod üretimini ve güçlü metaprogramlamayı destekler
  • Rust ile karşılaştırıldığında Zig, anotasyonlar ve açık kod yapısı sayesinde daha hassas optimizasyonlara olanak tanır
  • String karşılaştırma gibi tekrarlı işlemlerde comptime kullanılarak sıradan bir fonksiyondan daha iyi assembly kodu üretilebilir

Optimizasyon ve Zig

"Her şey mümkündür ama ilginç olanı kolay elde edilemez." şeklindeki ünlü uyarıda olduğu gibi, program optimizasyonu her zaman geliştiricilerin başlıca ilgi alanlarından biridir. Bulut altyapısı maliyetleri, gecikmeyi azaltma, sistem sadeleştirme gibi amaçlar için kod optimizasyonu zorunludur. Bu yazı, Zig'deki düşük seviyeli optimizasyon kavramını ve Zig'in güçlü yönlerini merkeze alarak açıklıyor.

Derleyiciye güvenebilir miyiz?

  • Genel olarak "derleyiciye güvenin" tavsiyesi sık verilir; ancak pratikte derleyici beklentiden farklı davranabilir ya da dil belirtimini ihlal eden durumlar olabilir
  • Yüksek seviyeli dillerde niyeti (intent) açık biçimde aktarmak zor olduğu için performans açısından kısıtlar oluşur
  • Düşük seviyeli dillerde kodun açık yapısı sayesinde derleyici optimizasyon için gerekli bilgileri anlayabilir; örneğin JavaScript ve Zig'deki maxArray fonksiyonları karşılaştırıldığında Zig, açık türler, hizalama, alias olup olmama gibi bilgileri çalışma zamanında değil derleme zamanında iletir
  • Aynı maxArray işlemi Zig ve Rust ile yazıldığında neredeyse aynı yüksek performanslı assembly kodu elde edilebilir; ancak niyet ne kadar iyi ifade edilirse optimizasyon sonucu da o kadar iyileşir
  • Yine de derleyici performansına her zaman güvenilemeyeceği için, darboğaz bölgelerinde kodu ve derleme çıktısını doğrudan inceleyip optimizasyon yolları aranmalıdır

Zig'in rolü

  • Zig; kesin açıklık, zengin yerleşik fonksiyonlar, pointer'lar ve anotasyonlar, comptime ve iyi tanımlanmış Illegal Behavior gibi özellikleri sayesinde soyut bilgilere ihtiyaç duymadan optimize edilmiş kod üretebilir
  • Rust, bellek modeli sayesinde varsayılan olarak argüman alias'ının olmadığını garanti eder; Zig'de ise doğrudan noalias gibi anotasyonlar gerekir
  • Yalnızca LLVM IR ölçüt alınırsa, Zig'in optimizasyon seviyesi de yüksektir
  • Hepsinden önemlisi, Zig'in comptime'ı (derleme zamanı yürütme) güçlü bir optimizasyon aracıdır

comptime nedir?

  • Zig'deki comptime, kod üretimi, sabit değer gömme, türe dayalı generic struct oluşturma gibi alanlarda kullanılır ve çalışma zamanı performansını artırmada önemli rol oynar
  • comptime ile metaprogramlama yapılabilir
  • C/C++ makroları veya Rust'un macro sistemiyle karşılaştırıldığında comptime, ayrı bir sözdizimi değil normal koddur
  • comptime kodu AST'yi doğrudan değiştirmez; bunun yerine tüm türler için derleme zamanında denetleme, yansıtma ve üretim yapabilir
  • comptime'ın esnekliği Rust gibi diğer dillerdeki iyileştirmeleri de etkilemiş ve Zig diline doğal biçimde entegre edilmiştir

comptime'ın sınırları

  • token-pasting gibi bazı macro özellikleri, Zig comptime ile bire bir karşılanamaz
  • Zig, kodun okunabilirliğine önem verdiği için kapsam dışına çıkıp değişken üretmeye veya makro tanımlamaya izin vermez
  • Bunun yerine Zig comptime için type reflection, DSL uygulamaları, string parsing optimizasyonu gibi geniş metaprogramlama kullanım örnekleri vardır

comptime ile string karşılaştırma optimizasyonu

  • Genel bir string karşılaştırma fonksiyonu her dilde yazılabilir; ancak Zig'de iki string'den biri comptime sırasında bilinen sabit olduğunda daha verimli assembly kodu üretilebilir
  • Örneğin bir string her zaman "Hello!\n" ise, bu değer bayt bayt değil daha büyük bloklar halinde karşılaştırılarak optimize edilebilir
  • Bunun için comptime kullanıldığında SIMD vektörleri, blok işleme ve kalan bayt optimizasyonu gibi yüksek performanslı kodlar derleme zamanında üretilebilir
  • Bu yaklaşım sayesinde yalnızca tekrarlı string karşılaştırmaları değil, statik veriye dayalı çeşitli eşlemeler, perfect hash tabloları, AST parser'ları gibi performans odaklı pek çok yapı da gerçekleştirilebilir

Sonuç

  • Zig, düşük seviyeli optimizasyon için son derece uygundur; açık kod yapısı ve güçlü comptime yetenekleri sayesinde en yüksek performans doğrudan uygulanabilir
  • Rust gibi diğer dillerle karşılaştırıldığında da Zig'in derleme zamanı programlama yeteneği ve açıklığı, yüksek performanslı yazılım geliştirmede büyük avantaj sağlar
  • Zig'in optimizasyon kabiliyeti, gelecekte daha da önemli bir rekabet avantajı olacaktır

1 yorum

 
GN⁺ 2025-06-08
Hacker News görüşleri
  • Zig'de en ilginç bulduğum şeyler build sisteminin sadeliği, cross-compilation ve yüksek iterasyon hızına odaklanması. Ben bir oyun geliştiricisiyim, bu yüzden performans önemli; ama çoğu gereksinim için çoğu dil yeterli performansı veriyor. Bu yüzden dil seçiminde birincil kriter bu değil. Herhangi bir dille güçlü kod yazılabilir, ama ben onlarca yıl bakımını yapabileceğim, geleceğe dönük framework'leri hedefliyorum. C/C++ her yerde desteklendiği için varsayılan seçenek oldu, ama Zig'in de o seviyeye gelebileceğini düşünüyorum
    • Eğlencesine çok eski bir Kindle cihazında (Linux 4.1.15) Zig çalıştırdım ve Zig'in olgunluk seviyesi beni şaşırttı. Çoğu şey doğrudan çalıştı ve eski bir GDB ile bile garip hataları debug edebildim. Ben de Zig'e hayran kaldım. Ayrıntılı deneyimimi burada görebilirsiniz
    • Çoğu dille güçlü kod yazılabileceğini düşünüyorum, ama onlarca yılı gözeten modüler kod istiyorum. Zig'i seviyorum, ancak uzun vadeli bakım ve modülerlik açısından eksileri olduğunu düşünüyorum. Zig encapsulation'a karşıt bir dil. Struct üyelerini private yapmak mümkün değil. Bu issue yorumunda örnek var. Zig'in yaklaşımı, ayrı bir iç temsil diye bir şey olmaması ve tüm kullanıcıların iç implementasyonu bilmesini sağlayacak şekilde her şeyin belgelenip açığa çıkarılması yönünde. Ama API sözleşmesini, yani modüler yazılımın özünü korumak için iç implementasyonu gizleyebilmek gerekir ve bu mümkün değil. Bir gün Zig'in private alanları desteklemesini umuyorum
    • Rust'ı biraz kullandım ve hoşuma gitti. Ama "kötü" olduğuna dair şeyler duyunca bir süre bırakıp şimdi tekrar kullanmaya başladım. Hâlâ hoşuma gidiyor. İnsanların neden bu kadar nefret ettiğini pek anlamıyorum. Çirkin generic sözdizimi C# ve TypeScript'te de var. Borrow Checker da düşük seviye dil deneyiminiz varsa anlaması kolay
    • Zig, daha basit bir Rust ve daha iyi bir Go gibi hissettiriyor. Öte yandan Zig ile yapılmış araçlar arasında bun'a gerçekten hayranım. bun hayatımı inanılmaz kolaylaştırdı. Rust tabanlı uv de benzer bir deneyim sunuyor
    • C/C++'ın temel seçenek olduğuna katılıyorum. C'den daha iyi bir şey yapmaya çalışanların çoğu sonunda yine C++ üretmiş oldu. Yine de denemeyi bırakmamak lazım. Rust ve Zig, hâlâ daha iyisini ummamız için birer kanıt. Ben de artık C++'ı daha fazla öğrenmeyi planlıyorum
  • En ileri derleyiciler bazen dil spesifikasyonunu ihlal ediyor gibi görünse de, Clang'in sonsuz döngünün biteceğini varsayması C11 ve sonrası standarda göre doğru. C11 bunu açıkça söylüyor: "Kontrol ifadesi sabit bir ifade değilse ve döngü girdi/çıktı, volatile, senkronizasyon veya atomic işlem yapmıyorsa, derleyici onun sonlanacağını varsayabilir"
    • C++'ta (ileride C++26'ya kadar) bu kural tüm döngüler için geçerli, ama dediğiniz gibi C dilinde yalnızca "kontrol ifadesi sabit ifade olmayan döngüler" için geçerli. Yani for(;;); gibi açıkça sonsuz olan bir döngü gerçekten sonsuz döngü olmak zorunda ve Rust'taki loop {} için de aynı şey geçerli olmalı. Ama LLVM geliştiricileri bazen sadece C++ derleyicisi yaptıklarını sanıyor; bu yüzden Rust'ta "lütfen sonsuz döngü" denince LLVM "C++'ta öyle bir şey olmaz, optimize edelim!" diye davranıp sorun çıkarıyor. Yani yanlış dile yanlış optimizasyon uygulanmış oluyor
  • Compile-time (comptime) özelliği olmasa bile string karşılaştırmasını inline etmek ve unroll etmek C'de de gayet mümkün. İlgili örnek
    • Tespit doğru! İlk örnek fazla basitti. Daha iyi bir örnek olarak compile-time suffix automaton verilebilir. Ayrıca yukarıda bağlantısı verilen godbolt kodu, aslında yapılmaması gereken iki şeyden birini gösteriyor
  • Örnek verilen JavaScript kodunda V8'in ürettiği bytecode'un verimsiz olduğu söylenmiş ama bunun iyi bir karşılaştırma örneği olduğunu düşünmüyorum. Zig ve Rust için çok yeni bir hedef ortam seçilerek derleme isteniyor, ama V8 tarafında böyle optimizasyon seçenekleri zorlanmıyor. Aslında modern JIT'ler de koşullar uygunsa vectorization yapabiliyor. Ayrıca çoğu modern dil de string optimizasyonlarını benzer şekilde ele alıyor. Referans olması için C++ örneği de var
    • Aslında JS ile Zig'i karşılaştırmak elmayla meyve salatasını karşılaştırmak gibi. Zig örneğinde türü ve boyutu sabit diziler var, JS ise çalışma zamanında farklı türlerin gelebileceği "generic" kod. Bu yüzden JS tarafında tür bilgisini yeterince verirseniz JIT çok daha hızlı döngüler üretebilir; vectorization olmasa bile. Pratikte TypedArray çok sık kullanılmıyor, çünkü başlatma maliyeti yüksek ve ancak tekrar tekrar kullanılacaksa anlamlı oluyor. Ayrıca yazıda JS kodunun şişirilmiş olduğu söylenmiş ama bunun büyük kısmı JIT'in dizi uzunluğu kontrolüne güvenemeyip guard eklemesinden kaynaklanıyor; oysa gerçekte herkes i < x.length gibi döngüler yazdığı için JIT optimizasyonu devreye giriyor. Bu açıdan biraz kusur arama gibi, ama yine de küçük bir fark var
    • Rust ve Zig'in godbolt örneklerini daha eski CPU'ları hedefleyecek şekilde de değiştirebilirsiniz. JS tarafındaki hedef kısıtını düşünmemiştim. Ve C++ örneği, clang'in ne kadar iyi kod ürettiğini gösteren bir örnek. Yine de şimdilik assembly pek tatmin edici görünmüyor; Zig'in belirli bir CPU hedefi için build edildiğini hesaba katsak bile. Compile-time Suffix Automaton için bir C++ portu örneği olursa gerçekten çok ilginç olurdu. Bu, C++ derleyicilerinin tahmin edemeyeceği türden gerçek bir comptime kullanım örneği
  • "Yüksek seviyeli dillerde düşük seviyeli dillerdeki 'intent' eksik" denmesi bana tuhaf geliyor. Bence yüksek seviyeli dillerin avantajı, niyeti çok daha fazla şekilde ve daha ayrıntılı ifade edebilmeleri
    • Ben de katılıyorum. Temelde yüksek seviyeli diller ile düşük seviyeli diller arasındaki fark şu: yüksek seviyeli dilde niyeti ifade edersiniz, düşük seviyeli dilde ise implementasyon mekanizmasının kendisini açığa vurmanız gerekir
    • Buradaki "niyet", "bu satın almanın vergisini hesapla" gibi iş düzeyi bir niyet değil; daha çok "bu byte'ı sola üç bit kaydır" gibi, bilgisayara tam olarak ne yaptırdığınıza yakın bir şey. Örneğin purchase.calculate_tax().await.map_err(|e| TaxCalculationError { source: e })?; gibi bir kod niyet doludur, ama bunun makine kodunda nasıl görüneceğini öngörmek mümkün değildir
  • Zig'in allocator modeli gerçekten çok hoşuma gidiyor. Keşke Go'da da GC yerine istek başına allocator gibi şeyler kullanabilsek
    • Go'da custom allocator ve arena tamamen imkânsız değil, ama kullanımı çok kötü ve doğru şekilde kullanması zor. Dil seviyesinde ownership kurallarını ifade etmenin ya da zorlamanın da bir yolu yok. Sonuçta sadece sözdizimi biraz farklı bir C yazmış oluyorsunuz ve GC olmayınca C++'tan bile daha tehlikeli hâle geliyor
  • "Zig'in verbosity'sini seviyorum" sözüne katılıyorum ama dürüst olmak gerekirse kulağa biraz tuhaf geliyor. C pek çok yerde gevşek davranırken, Zig bunun tersine çoğu durumda fazla miktarda annotation noise gerektiriyor; özellikle matematiksel ifadelerde açık integer cast'leri gerektiğinde. Bununla ilgili olarak şu yazıya bakabilirsiniz. Performans tarafında Zig'in C'den hızlı olduğu durumlar çoğunlukla Zig'in daha agresif LLVM optimizasyon ayarları kullanmasından kaynaklanıyor (-march=native, tüm program optimizasyonu vb.). Aslında C'de de unreachable gibi optimizasyon ipuçları dil uzantılarıyla mümkün ve Clang de constant folding konusunda çok agresif. Yani Zig'in comptime'ı ile C'nin codegen'i arasındaki farklar çoğu zaman derleyici optimizasyon ayarlarından doğuyor. TL;DR: C yavaşsa önce derleyici ayarlarını kontrol edin. Sonuçta optimizasyonun kalbi LLVM
    • Cast örneği için, aslında cast işlemini saran tek bir fonksiyon yazmak kodun yeniden kullanılabilirliğini ve niyet açıklığını artırabilir
      fn signExtendCast(comptime T: type, x: anytype) T {
        const ST = std.meta.Int(.signed, @bitSizeOf(T));
        const SX = std.meta.Int(.signed, @bitSizeOf(@TypeOf(x)));
        return @bitCast(@as(ST, @as(SX, @bitCast(x))));
      }
      export fn addi8(addr: u16, offset: u8) u16 {
        return addr +% signExtendCast(u16, offset);
      }
      
      Bu yöntem de aynı assembly'yi üretiyor; ayrıca daha kullanışlı ve daha açık
    • Zig'in fikirleri ilginç ve orijinal yazıdan beklediğimden daha fazla compile-time (comptime) ve tüm program derlemeye ağırlık veriyordu. Buna katılıyorum. Bu arada Virgil, 2006'dan beri compile-time'da tüm dili kullanma ve tüm program derleme desteği sunuyordu. Virgil LLVM'i hedeflemediği için hız karşılaştırması sonunda backend karşılaştırmasına dönüyor. Virgil bu yaklaşım sayesinde method call'ları önceden statik olarak bağlayabiliyor (devirtualize), kullanılmayan alanları/nesneleri büyük ölçüde temizleyebiliyor, hatta alanlardan heap nesnelerine kadar sabit yayılımı yapabiliyor ve tamamen özelleştirme sağlayabiliyor
    • Gelecekte AI kullanımını düşünürsek, giderek daha açık ve daha ayrıntılı dillerin öne çıkacağını sanıyorum. İnsanlar AI ile kod yazıyor mu, bu doğru mu, bunlardan bağımsız olarak pek çok geliştirici AI yardımını tercih ettikçe diller de buna göre değişecek
    • Yeni x86 backend gelirse, gelecekte C ile Zig arasındaki performans farkının Zig projesinin kendisinden kaynaklandığı örnekleri de görebiliriz
    • Açık integer cast'leri konusunda yakında biraz daha temiz bir iyileştirme gelmesi bekleniyor. İlgili tartışmaya bakabilirsiniz
  • "C, Python'dan hızlıdır" gibi dili kendi başına benchmark etmek doğru değil, ama bir dilin bazı özellikleri optimizasyonun önünde ciddi engel oluşturabiliyor. Uygun dili kullanırsanız hem geliştirici hem de derleyici niyeti doğal ve hızlı bir biçimde ifade edebilir
  • Zig'in for loop sözdizimi bana çok dağınık geliyor. İki listeyi yan yana koyup hizalamak zorunda kalmak, sadece bakarken bile gözü yoruyor. Son dönem dillerin çok fazla "sihirli" sözdizimi ve özel sembol doldurmasının hata olduğunu düşünüyorum. Saatlerce bakmak isteyeceğim bir şey değil
    • Bu tür iki diziyi birlikte dolaşma kalıbı düşük seviyeli kodda çok yaygın, paralel dolaşım da öyle. Bu yüzden Zig'in bunu açık ve doğal şekilde desteklemesi bence gayet uygun. Neden gözü yorucu geldiğini merak ediyorum
  • Optimizasyon çok önemli. Etkisi zaman geçtikçe daha da büyür
    • Tabii bu, o yazılımın gerçekten kullanılması şartıyla geçerli