2 puan yazan GN⁺ 2025-10-28 | 1 yorum | WhatsApp'ta paylaş
  • Yazar, Zig dilini öğrenirken AcoustID indeksini yeniden yazma projesi üzerinde çalıştı ve ağ programlamasındaki sınırlamalar nedeniyle yeni bir yaklaşım denedi
  • Daha önce C++ ve Go'da kullandığı asenkron I/O ve eşzamanlılık modelini Zig'de de uygulamak için kendi kütüphanesini geliştirmeye karar verdi
  • Bunun sonucunda, Go tarzı eşzamanlılık modelini Zig'e uygun şekilde uygulayan Zio kütüphanesini geliştirerek callback olmadan senkronmuş gibi görünen asenkron kod yazmayı mümkün kıldı
  • Zio; asenkron ağ ve dosya I/O'su, kanallar, senkronizasyon primitifleri, sinyal izleme gibi özellikleri destekliyor ve tek iş parçacıklı modda Go veya Rust'ın Tokio'sundan daha hızlı performans gösteriyor
  • Bu proje, Zig'in sistem düzeyi performans ile modern eşzamanlılık modelini birleştirme potansiyelini gösterirken, Zig ekosisteminin genişlemesi açısından önemli bir dönüm noktası olarak değerlendiriliyor

Zig dili ve ilk motivasyon

  • Yazar, başlangıçta ses yazılımları için düşük seviyeli bir dil olarak tasarlanan Zig'i izliyordu, ancak somut bir ihtiyaç hissetmiyordu
    • Zig'in yaratıcısı Andrew Kelley'nin yazarın Chromaprint algoritmasını Zig ile yeniden uyguladığını görünce ilgisi arttı
  • AcoustID'nin ters indeks yeniden yazım projesini Zig öğrenmek için bir fırsat olarak ele aldı ve sonuçta C++ sürümünden daha hızlı ve daha ölçeklenebilir bir uygulama elde etti
  • Ancak sunucu arayüzü ekleme aşamasında asenkron ağ desteğinin yetersizliği sorunuyla karşılaştı

Mevcut yaklaşımlar ve sınırlamalar

  • Önceki C++ sürümünde asenkron I/O için Qt framework'ü kullanıyordu; callback tabanlı olsa da sunduğu zengin destek sayesinde kullanılabiliyordu
  • Daha sonraki prototiplerde Go dilinin ağ ve eşzamanlılık kolaylığından yararlandı, ancak Zig'de benzer düzeyde soyutlama bulunmuyordu
  • Zig ile TCP sunucusu ve küme katmanı uygulamak, çok sayıda iş parçacığı oluşturmayı gerektiren verimsiz bir yapıya yol açıyordu
    • Bunu çözmek için NATS mesajlaşma sistemi için Zig istemcisini (nats.zig) doğrudan yazarak Zig'in ağ özelliklerini derinlemesine inceledi

Zio kütüphanesinin ortaya çıkışı

  • Bu deneyimlerin ardından Zio: Zig için asenkron I/O ve eşzamanlılık kütüphanesini duyurdu
  • Zio, callback'siz asenkron kod yazmayı hedefliyor; içeride asenkron I/O çalışırken dışarıdan senkronmuş gibi görünen bir yapı sunuyor
  • Go tarzı eşzamanlılık modelini Zig'e uygun biçimde sınırlı olarak uyguluyor
    • Zio'daki task'ler, sabit boyutlu stack'e sahip stackful coroutine'ler biçiminde çalışıyor
    • stream.read() çağrıldığında I/O işi arka planda yürütülüyor, tamamlanınca task yeniden başlatılıyor ve sonuç döndürülüyor
  • Bu yaklaşım aynı anda hem durum yönetimini basitleştiriyor hem de kod okunabilirliğini artırıyor

Özellik seti ve runtime yapısı

  • Zio; tam asenkron ağ ve dosya I/O'su, senkronizasyon primitifleri (mutex, condition variable vb.), Go tarzı kanallar, OS sinyal izleme gibi özellikleri destekliyor
  • Task'ler tek iş parçacıklı veya çok iş parçacıklı modda çalıştırılabiliyor
    • Çok iş parçacıklı modda task'ler iş parçacıkları arasında taşınabiliyor; bu da gecikmeyi azaltma ve yük dengelemesini iyileştirme etkisi sağlıyor
  • Standart Reader/Writer arayüzlerini uygulayarak dış kütüphanelerle uyumluluk sağlıyor

Performans ve karşılaştırma

  • Yazar henüz resmi benchmark yayımlamış değil, ancak tek iş parçacıklı modda Go ve Rust'ın Tokio'sundan daha hızlı performans gördüğünü belirtiyor
  • Context switch maliyeti fonksiyon çağrısı seviyesine kadar düşük; geçiş hızı neredeyse ücretsiz denecek kadar yüksek
  • Çok iş parçacıklı mod henüz Go/Tokio kadar sağlam değil, ancak benzer veya biraz daha iyi performans gösteriyor
    • İleride fairness özelliği eklendiğinde performansın bir miktar düşme ihtimali var

Örnek kod ve kullanım

  • Belgede Zio tabanlı bir HTTP sunucusu örnek kodu yer alıyor
    • zio.net.Stream ile bağlantılar kabul ediliyor ve her bağlantı ayrı bir task tarafından işleniyor
    • zio.Runtime, task yürütme ile I/O zamanlamasını yönetiyor
  • Bu yapı, asenkron I/O'nun senkron kod gibi yazılmasını sağlarken akış kontrolünü ve kaynak serbest bırakmayı daha net biçimde yönetmeye olanak tanıyor

Gelecek planları ve önemi

  • Yazar, Zio sayesinde Zig'in yalnızca yüksek performanslı sistem kodu için bir dil olmanın ötesine geçip, tam teşekküllü bir ağ uygulaması geliştirme dili haline gelebileceğini gördüğünü söylüyor
  • Bir sonraki adım olarak NATS istemcisini Zio tabanlı yeniden yazmayı ve Zio tabanlı HTTP istemci/sunucu kütüphaneleri geliştirmeyi planlıyor
  • Bu proje, Zig ekosisteminin ağ ve eşzamanlılık altyapısının genişlemesine öncülük ederken, Go ve Rust ile kıyaslanabilecek modern bir runtime modeli kurma girişimi olarak değerlendiriliyor

1 yorum

 
GN⁺ 2025-10-28
Hacker News görüşleri
  • Bağlam değiştirmenin işlev çağrısı düzeyinde neredeyse ücretsiz olduğu söylense de, pratikte dal tahmincisinin (branch predictor) bozulması gibi ince maliyetler var
    Zig'in async tasarımının donanım düzeyindeki call/return çiftlerini mi kullandığı, yoksa dolaylı atlama temelli mi derlendiği net değil
    Kusursuz bir benchmark için, iki görev arasında sürekli geçiş olan bir programın toplam çalışma süresiyle tamamen senkron bir programınkini karşılaştırmak gerekir. Bu da epey zor bir iştir
    • Stackless coroutine'lerde çağrı yığınının en altından iki görev sürekli değiştiriliyor ve yığın değiştirme kodu inline ediliyorsa, call/ret uyumsuzluğu cezasından büyük ölçüde kaçınılabilir
      Derleyiciyi kontrol edebiliyorsanız, I/O kodundaki call/ret'i açık sıçramalara dönüştürmek de mümkündür
      Uzun vadede CPU'nun stackful coroutine'leri daha iyi tahmin etmesi için bir meta-predictor eklenmesini umuyorum
    • Zig'de şu anda async dil seviyesinde kaldırıldı; OP ise kullanıcı alanında görev değiştirmeyi doğrudan kendisi uygulamış
    • Coroutine'ler arasında basit bir ping-pong testi yaptığımda, diğer çözümlerle kıyaslandığında inanması güç rakamlar elde ettiğim olmuştu
    • Yakında Zig'e yeni bir async ekleneceği için, derinlemesine incelemeden önce bekliyorum
      İlgili yazı: Zig new async I/O
  • Stackful coroutine'ler, RAM yeterliyse anlamlıdır
    Ben Zig'i embedded (ARM Cortex-M4, 256KB RAM) ortamında kullanıyorum ve C ile birlikte çalışmada bellek güvenliğini sağlamak için tercih ediyorum
    Rust'taki gibi renkli async'i daha çok seviyorum. Senkron kod gibi görünen o sihirli his güzel, ama büyük kod tabanlarında hangi işlevin blocking olduğunu ayırt etmek zorlaşıyor
    • Aslında tüm senkron kod, yazılımın yarattığı bir illüzyondur
      CPU I/O yüzünden gerçekten bloklanmaz; OS thread'leri de aslında işletim sisteminin uyguladığı stackful coroutine'lerdir
      Dil düzeyinde bu illüzyonu daha verimli uygulayabilmemiz dışında öz değişmiyor
    • Yeni Zig IO, Rust'tan daha zarif bir şekilde renklendirilmiş (colored) bir yapı olacak
      Bir işlevin renkli olup olmadığı, I/O yapıp yapmamasına göre belirlenecek; çağrı anında da async olup olmadığı açıkça belirtilecek
      Zig ayrıca işlev çağrısı sırasında gereken yığın boyutunu hesaplamayı da hedefliyor; bu sayede stackful coroutine'lerin RAM israfı sorununu azaltabileceği umuluyor
    • Zig'in I/O'yu açıkça ifade etmeye çalışmasının nedeni de tam olarak bu: hangi işlevlerin blocking olduğunu izleyebilmek
  • Zig'i şu anda benimsemek için erken olduğunu söyleyenler var. I/O modeli büyük bir değişimden geçtiği için bunun birkaç yıl süreceği izlenimi var
    • Ben de 2020'de benzer nedenlerle Zig'den uzaklaşmıştım.
      Yine de proje hâlâ çok aktif ve hızlı sürüm çıkarmaktan çok doğru tasarımı öncelemesini olumlu buluyorum
      Şimdilik Go ya da C kullanıp 1.0'ı bekliyorum
    • Birkaç yıl çabuk geçer. Zig zaten yeterince kullanılabilir bir dil. Kullanacak olan kullanır, kullanmayacak olan kullanmaz
    • Gerçekte kötü bir zamana denk geliyor. 0.16'da büyük I/O değişiklikleri planlanıyor ve yazarın kendisi bile en yeni özellikleri henüz kullanmıyor
      Ben de I/O ağırlıklı işler için 0.16'yı bekleyeceğim
    • Ama I/O ile ilgili işlerde Zig 0.15'in buffered reader/writer interface'ini kullanırsanız, çok büyük bir değişiklik olmayacaktır
    • Bence tam tersine şu an yanlış bir zaman değil. Zig dilinin kendisi kökten değişmiyor; yalnızca yeni ve güçlü bir std.Io API'si ekleniyor
      Mevcut kod çalışmaya devam ediyor ve yeni API daha ergonomik ve performanslı
      Ben de mevcut projeyi yeni Reader/Writer API'sine taşırken kodun çok daha temiz hâle geldiğini gördüm
  • Callback tabanlı async'in neden standart hâline geldiği hâlâ bir muamma
    libtask benzeri yaklaşım çok daha temiz görünüyor
    Rust da callback tabanlı async'i benimsedi ama nedenini pek anlayamıyorum
    Referans: libtask
    • Stackless coroutine'ler dilin içinde uygulanabilir ve mevcut özelliklerle öngörülebilir etkileşim sunmaları avantajdır
      Ama yığını doğrudan ele aldığınızda exception handling, GC, debugger gibi şeylerle çakışabilir
      Ayrıca LLVM düzeyinde bu tür değişiklikleri birleştirmek de zor olduğundan, dil tasarımcıları açısından pratik kısıtlar fazladır
    • Microsoft'un C++ standardı için yaptığı araştırmalarda, stackless coroutine'lerin bellek ek yükünün çok daha az olduğu ve yürütücü tasarımında daha fazla özgürlük verdiği sonucuna varıldı
    • zio ya da libtask yaklaşımının dezavantajı, yığın boyutunu elle tahmin etmek zorunda olmanızdır
      Çok küçük olursa taşma, çok büyük olursa bellek israfı olur
      Gerekli yığın boyutu platformdan platforma değiştiği için taşınabilirlik sorunu da doğar
      Zig sorunu #157 çözülürse, bu yaklaşım daha iyi hâle gelebilir
    • libtask gibi çözümlerde thread stack boyutu belirsizdir ve tipik async durumundan çok daha büyüktür
    • Rust'ın async'i callback değil, polling tabanlıdır
      Yani async'i uygulamanın üç yolu vardır
      1. Callback tabanlı (Node.js, Swift)
      2. Stackful tabanlı (Go, libtask)
      3. Polling tabanlı (Rust)
        Rust statik bir durum makinesine dönüştürülür ve runtime bunu poll eder
        Stackful yaklaşımda bellek israfı büyüktür ve yığın boyutunu yönetmek zordur
        Rust bunu önlemek için stackless bir yapı seçti; Zig ise iki yaklaşım arasında seçim yapılmasına izin vermeyi planlıyor
        Referans: zio coroutine kodu
  • TCP okuması bir ay boyunca bloklanabilir; bu durumda I/O timeout arayüzünün nasıl olacağını merak ediyorum
    • TCP soketlerinde setsockopt ile okuma/yazma timeout'u ayarlayabilirsiniz
      Zig bir POSIX API katmanı sağlıyor
      Referans: setsockopt belgesi
    • Zig'in mevcut std.Io.Reader'ı timeout farkındalığına sahip değil
      Python'daki asyncio.timeout gibi çalışan bir yapı tasarlanıyor
      Örnek kod:
      var timeout: zio.Timeout = .init;
      defer timeout.cancel(rt);
      timeout.set(rt, 10);
      const n = try reader.interface.readVec(&data);
      
    • Çoğu async framework, timeout ve iptali göz ardı ediyor
      Oysa en zor kısım aslında tam da bu
  • Scala'da zaten ZIO adında bir eşzamanlılık kütüphanesi var
    Referans: zio.dev
  • Son zamanlarda Rust'ın Tokio'sundan çok etkilendim; Zig'de de Go tarzı eşzamanlılığı GC olmadan uygulamak mümkünse mutlaka denemek isterim
    • Go, GC sayesinde sonsuzca genişleyebilen yığınlar gibi numaralar kullanabiliyor
      Ama Zig, düşük seviyeli bir dil olmasına rağmen yüksek seviyeli API'leri temiz şekilde ifade edebilmesiyle beni etkilemişti
  • Zig'i ilk kez Bun web sitesinde görmüştüm. Bu aralar gerçekten çok hızlı ilerliyor
  • Eski C++ sürümünde Qt ile asenkron I/O uygulamıştım, bu kez ise Go'ya geçtim
    Hem Zig hem de Go için yeni Qt binding'leri çıktı
    • Go: miqt
    • Zig: libqt6zig
      Ben Rust için binding istiyorum. Yalnızca cxx-qt aktif olarak sürdürülüyor ama QML ya da CMake kullanmak istemiyorum. Qt'yi yalnızca Rust + Cargo ile kullanmak istiyorum
  • Scala'da zaten meşhur ZIO framework'ü var; isim bulmanın gerçekten ne kadar zor olduğunu düşündürüyor