2 puan yazan GN⁺ 2024-11-30 | 1 yorum | WhatsApp'ta paylaş

Benchmark

  • Coroutine nedir?

    • Coroutine, bir programın yürütülmesini geçici olarak durdurup yeniden başlatabilen bir bilgisayar programı bileşenidir; işbirlikçi çoklu görev için alt yordamların genelleştirilmiş hâlidir.
    • İşbirlikçi işler, istisnalar, event loop’lar, yineleyiciler, sonsuz listeler ve pipe gibi program bileşenlerini uygulamak için uygundur.
  • Rust

    • İki program yazıldı: tokio ve async_std kullanan programlar.
    • Her ikisi de Rust’ta yaygın olarak kullanılan asenkron runtime’lardır.
  • C#

    • C#, Rust’a benzer şekilde async/await desteği sunar.
    • .NET 7’den itibaren NativeAOT derlemesi sunarak VM olmadan da yönetilen kodun çalıştırılmasını mümkün kılar.
  • NodeJS

    • Asenkron işler için Promise.all kullanıldı.
  • Python

    • Asenkron işleri yürütmek için asyncio modülü kullanıldı.
  • Go

    • Eşzamanlılık, goroutine’ler ile uygulanır ve işleri beklemek için WaitGroup kullanılır.
  • Java

    • JDK 21’den itibaren goroutine’lere benzer bir kavram olan sanal thread’ler sunuluyor.
    • GraalVM kullanılarak native image üretilebiliyor.

Test ortamı

  • Donanım: 13. nesil Intel(R) Core(TM) i7-13700K
  • İşletim sistemi: Debian GNU/Linux 12 (bookworm)
  • Rust: 1.82.0
  • .NET: 9.0.100
  • Go: 1.23.3
  • Java: openjdk 23.0.1
  • Java (GraalVM): java 23.0.1
  • NodeJS: v23.2.0
  • Python: 3.13.0

Sonuçlar

  • En düşük bellek kullanımı

    • Rust, C# (NativeAOT) ve Go, native binary olarak derlendiği için az bellek kullanıyor.
    • Java (GraalVM native image) da iyi performans gösterdi, ancak diğer statik derlenen dillere göre daha fazla bellek kullandı.
  • 10 bin iş

    • Rust’ta bellek kullanımı neredeyse hiç artmıyor.
    • C# (NativeAOT) da az bellek kullanıyor.
    • Go, beklenenden daha fazla bellek kullanıyor.
  • 100 bin iş

    • Rust ve C# iyi performans gösteriyor.
    • C# (NativeAOT), Rust’tan daha az bellek kullanıyor.
  • 1 milyon iş

    • C#, tüm dilleri açık farkla geride bırakarak en az belleği kullanıyor.
    • Rust da bellek verimliliğinde çok başarılı.
    • Go, diğer dillere kıyasla daha fazla bellek kullanıyor.

Sonuç

  • Çok sayıda eşzamanlı iş, karmaşık işlemler yapmasa bile önemli miktarda bellek tüketebilir.
  • .NET ve NativeAOT’taki iyileştirmeler dikkat çekici; GraalVM ile derlenen Java native image da bellek verimliliğinde oldukça başarılı.
  • Goroutine’ler kaynak tüketimi açısından hâlâ verimsiz.

Ek

  • Rust (tokio) tarafında join_all yerine for döngüsü kullanılarak bellek kullanımı yarıya indirildi. Rust, bu benchmark’ta mutlak liderliği ele geçirdi.

1 yorum

 
GN⁺ 2024-11-30
Hacker News yorumları
  • Benchmark, Node ile Go'nun asenkron işleme biçimleri arasındaki farkı yeterince yansıtmıyor. Node Promise.all kullanırken Go goroutine kullanıyor; bu yüzden fark oluşuyor. Asenkron I/O ile CPU-bound işlerin bellek kullanım farkını karşılaştırmak ilginç olurdu

  • "10 saniye bekleyen iş" ile "10 saniye sonra uyandırılan iş" arasındaki fark açıklanıyor. Go kodunun bellek kullanımı diğer kodlarla karşılaştırıldığında oldukça farklı

  • Go ile Node'u adil biçimde karşılaştırmak için, zamanlayıcı planlayan bir goroutine ve zamanlayıcı sinyalini işleyen bir goroutine kullanma yöntemi öneriliyor. Node'a Bun ve Deno'nun dahil edilmemiş olması tuhaf bulunuyor

  • Çok sayıda eşzamanlı iş çok bellek tüketebilir, ancak iş başına veri birkaç KB'yi aşıyorsa zamanlayıcının bellek ek yükü göz ardı edilebilir düzeyde kalır

  • "Eşzamanlı iş" tanımına göre bellek kullanımı değişebilir. Verimli bir uygulamada 1M eşzamanlı iş için yaklaşık 200MB gerekir

  • Go'nun bellek kullanımında Java'nın iki kattan fazla gerisinde kaldığına dikkat çekiliyor ve benchmark'ın gerçek programları temsil etmediği söyleniyor

  • Basit kodla dilleri karşılaştırmanın geliştiriciler açısından adil olmayabileceği, gerçek işler eklenerek bellek kullanımı ve zamanlama farklarının ölçülmesi gerektiği öneriliyor

  • Benchmark'ların sık sık hatalarla dolu olduğu ve bu tür benchmark'ları yayımlayan insanların motivasyonunun anlaşılamadığı söyleniyor

  • Java benchmark'ının hatalı olabileceği, ArrayList için başlangıç boyutu belirtilmediğinden gereksiz yere çok sayıda nesne oluşturulduğu belirtiliyor

  • Rust'un asenkron kodunun neden beklenenden daha hızlı tamamlandığı açıklanıyor. tokio::time::sleep() geleceğin oluşturulduğu zamanı takip ettiği için böyle oluyor