3 puan yazan GN⁺ 2026-01-03 | 2 yorum | WhatsApp'ta paylaş
  • Bundler’ın performans sınırları analiz ediliyor ve Python paket yöneticisi uv’nin neden hızlı olduğu karşılaştırılıyor
  • uv’nin hızı Rust dilinden değil; paralel indirme, global önbellek, metadata tabanlı bağımlılık işleme gibi yapısal tasarım tercihlerinden kaynaklanıyor
  • Bundler’da indirme ve kurulum süreci birbirine bağlı olduğu için paralel işlem kısıtlanıyor; bunların ayrılması büyük iyileştirme sağlayabilir
  • Global önbellek entegrasyonu, hardlink ile kurulum, PubGrub çözücüsünün entegrasyonu gibi adımlarla RubyGems ve Bundler arasındaki tekrar azaltılabilir
  • Dili yeniden yazmadan da performans artışının büyük kısmı Ruby kodu içinde elde edilebilir ve uv seviyesine yakın hızlara ulaşılabilir

Bundler ve uv performans karşılaştırması

  • RailsWorld’de sorulan “Bundler neden uv kadar hızlı değil?” sorusundan yola çıkılarak Bundler’ın performans darboğazları inceleniyor
  • Yazar, Bundler’ın uv seviyesinde hıza ulaşabileceğine inandığını ve performans farkının dilden değil tasarımdan kaynaklandığını açıkça belirtiyor
  • Andrew Nesbitt’in “How uv got so fast” yazısına atıfla, uv’nin temel optimizasyonlarının Bundler’a uygulanıp uygulanamayacağı analiz ediliyor

Rust’a yeniden yazma meselesi

  • uv’nin Rust ile yazılmış olması doğru, ancak hızın asıl nedeni Rust’ın kendisi değil
  • Bundler’daki darboğazlar giderilip “geriye kalan tek iyileştirme seçeneği Rust’a yeniden yazmak” olursa, bunun bir başarı sayılacağı belirtiliyor
  • Rust’a yeniden yazmak, mevcut uyumluluk kısıtları olmadan deneysel tasarımlar deneme özgürlüğü sunuyor; ancak zorunlu bir koşul değil

Bundler’ın yapısal darboğazları

  • Bundler, gem indirme ve kurulumunu tek bir metoda bağladığı için paralel indirme mümkün olmuyor
    • Örnek kodda install metodu, fetch_gem_if_not_cached ve install adımlarını art arda çalıştırıyor
    • Bu nedenle bağımlılık ilişkisi olan gem’ler (a -> b -> c) yalnızca sıralı olarak kurulabiliyor
  • Deney sonuçlarına göre bağımlılık olduğunda işlem 9 saniyeden uzun sürerken, bağımsız gem’ler (d, e, f) paralel indirme ile 4 saniyeden kısa sürede tamamlanıyor
  • İndirme ve kurulum ayrılırsa, bağımlılık kuralları korunurken paralel işlem de mümkün hale geliyor
    • Dört aşamalı bir ayrım öneriliyor: indirme → arşivden çıkarma → derleme → kurulum
    • Saf Ruby gem’lerde bağımlılık kurulum sırası gevşetilerek ek hız kazanımı sağlanabilir

Önbellek ve kurulum optimizasyonu

  • uv’nin global önbellek ve hardlink ile kurulum yaklaşımı Bundler’a da uygulanabilir
    • Bundler ve RubyGems şu anda Ruby sürümüne göre ayrı önbellekler kullanıyor
    • $XDG_CACHE_HOME tabanlı paylaşımlı bir önbelleğe geçilmesi gerekiyor
    • Hardlink ile kurulum da önbellek birleştirildikten sonra uygulanabilir
  • Bundler zaten PubGrub bağımlılık çözücüsünü kullanıyor, ancak RubyGems hâlâ molinillo kullanıyor
    • İki sistemin çözücülerinin birleştirilmesi, teknik borcu azaltmanın temel anahtarı olarak görülüyor

Rust bağlantılı optimizasyonların uygulanabilirliği

  • Zero-copy deserialization, RubyGems’in YAML ayrıştırma aşamasında kısmen uygulanabilir olabilir
  • Ruby’nin GVL’si (Global VM Lock), IO ağırlıklı işlerde paralelliğe büyük bir engel oluşturmuyor
    • IO ve ZLIB işlemleri sırasında GVL serbest bırakıldığı için paralel çalışma mümkün
    • Ancak küçük dosya yazımlarında GVL yönetim ek yükü performansı düşüren bir etken
    • Ruby içinde bunu iyileştirmeye yönelik çalışmalar sürüyor
  • Sürüm karşılaştırma optimizasyonu: uv, sürümleri u64 tamsayısına kodlayarak karşılaştırma hızını artırıyor
    • Ruby’de de Gem::Version değerlerini tamsayı tabanlı yapıya dönüştürerek çözücü performansı artırılabilir
    • Bununla ilgili refactoring girişimleri olmuş olsa da geriye dönük uyumluluk sorunları nedeniyle ertelenmiş

Sonuç ve gelecek planları

  • uv’nin hızı, dilden çok gereksiz işleri ortadan kaldıran tasarımından geliyor; Bundler da aynı yönde geliştirilebilir
  • RubyGems ve Bundler zaten modern bir paket yönetimi yapısına sahip olduğu için, uv seviyesinde hıza ulaşmak gerçekçi görülüyor
  • En büyük zorluk eski kod ve uyumluluğun korunması
  • Rust’a yeniden yazmadan da performans artışının %99’u Ruby kodu içinde sağlanabilir; kalan %1 ise önemsiz düzeyde
  • Devam yazısında Bundler ve RubyGems için gerçek profiling sonuçları ve somut darboğaz nedenleri ele alınacak

2 yorum

 
iolothebard 2026-01-06

Konuşmak ucuz. Kodu göster bana!

 
GN⁺ 2026-01-03
Hacker News görüşleri
  • Bundler’ın yapısını çok iyi bildiğimi söyleyemem ama en büyük iyileşmenin uv’nin önbellek tasarımını benimsemek olacağını düşünüyorum
    uv’nin hızlı olmasının temel nedenlerinden biri önbellek yapısı ve bu başka dillerde ya da ekosistemlerde de kopyalanabilir
    Ancak requires-python üst sınırını yok sayması performans için değil, daha iyi bir bağımlılık çözümü sağlamak için
    Örneğin bir proje Python 3.8 ve üzerini gerektiriyorsa ama bir bağımlılık <4 kısıtı koyuyorsa, Python 4’te kurulum yapılamaz
    uv tüm desteklenen sürümler için çözüm yaptığı için üst sınırı yok saymanın zaman açısından neredeyse hiçbir kazancı yok
    İlgili tartışma Python Discuss forumunda görülebilir

  • PEP 658’den sonra Python’ın Simple Repository API’si meta veriyi doğrudan sağlıyor; RubyGems.org da zaten benzer bilgileri sunuyor
    Ama bir gem’i açmadan native extension içerip içermediğini anlayamıyorsunuz
    Bu yüzden bu bilgi doğrudan RubyGems.org meta verisine eklenirse bağımlılık kurulum ağacının tamamen paralelleştirilebileceği öneriliyor

    • Ben de aynı şeyi düşünmüştüm ama gemspec bilgileriyle RubyGems.org meta verisinin farklı olma ihtimali var
      Eskiden RubyGems.org’da çalışırken meta verinin sürüm bazında çıkarıldığını hatırlıyorum
      Eski sürümlerin gemspec dosyalarını yeniden işlemek gerekir; bu da riskli bir meta veri değişikliği olabilir
      Bu yüzden geçmiş sürümlerde uygulamak zor olabilir ama gelecekte açma işlemi olmadan kurulum sırasını belirlemeyi mümkün kılacak iyileştirmeler yapılabilir gibi görünüyor
  • Aaron’ın Bundler’ı Rust ile yeniden yazmak yerine gerçek algoritmik iyileştirmelere odaklanmasını seviyorum

    • Hız artışı güzel ama benim için Ruby kurulumunu doğrudan yöneten özellikler daha gerekli
      Birden fazla sürüm yöneticisi aracı ve Ruby sürümünün birbirine karıştığı bu karmaşık ortam gerçekten çok yorucu
    • Aaron Shopify’da olduğu için gem.coop projesinden hiç bahsetmemesi bende karışık duygular uyandırıyor
      Sorunun sadece hız değil, kontrol ve ekosistemin yönü olduğunu düşünüyorum
      Ruby son 10 yılda hıza odaklandı ama belge kalitesi ve topluluk yönetimi aslında daha önemliydi
      Dilin neden gerilediğini ciddi biçimde düşünmenin ve farklı fikirleri zorlamanın zamanı geldi
  • Yakın tarihli ilgili bir yazı olarak How uv got so fast var (Aralık 2025, 457 yorum)

  • RubyGems’i daha hızlı yapmak için her gem’in dosya listesini kayıt/veritabanı haline getirmek kritik
    Böylece her require çağrısında dosya sistemini taramak gerekmez
    Bir gem doğrudan değiştirilirse meta veriyi yeniden hash’lemek gerekir ama zaten elle değiştirme tavsiye edilmiyor

    • Zamanında buna benzer bir kod yazmıştım; disk önbelleği yoktu ama hash’i anında üretmek bile ciddi hız artışı sağlamıştı
      Şimdi eski kalmış olabilir ama hâlâ sevdiğim küçük bir proje
      Kod: fastup
    • “bundle install” optimizasyonu yanlış yöne bakan bir yaklaşım
      Asıl sorun, $LOAD_PATH’in tüm gem’leri ekleyerek kombinatoryal patlama yaratması
      Birden fazla önbellek projesinin var olması bunun gerçek bir problem olduğunun kanıtı
      Eskiden bir uygulamanın başlaması dakikalar sürüyordu; load path ile oynayarak bunu dakikalar seviyesinde kısalttığım olmuştu
    • Bunu çalışma zamanında çözmeye çalıştım ama Ruby’de verimli veri yapıları eksik olduğu için uygulaması zordu
    • Aslında bunu zaten bootsnap yapıyor
      Zamanında bootsnap’i bundler’a entegre etmeyi önermiştim ama reddedilmişti
  • RubyGems’in yapısına dair açıklama ilginçti
    Gem bir tar dosyası ve içindeki YAML GemSpec bağımlılıkları tanımlıyor
    RubyGems.org bu bilgiyi API üzerinden sunduğu için eval kullanmadan da bağımlılıkları kontrol etmek mümkün
    Ancak YAML, ayrıştırma açısından verimsiz bir format; JSON ya da protobuf gibi alternatifler daha iyi olabilir
    Yine de gemserver zaten bağımlılık bilgisini döndürüyorsa büyük bir sorun olmayabilir

    • YAML çok iyi değil ama tipik bir gemspec boyutunda performans etkisinin ihmal edilebilir olacağını düşünüyorum
    • İnsanların düzenlemediği, sadece gözden geçirdiği bir lockfile ise YAML’ın karmaşık özelliklerini kaldıran basit bir ayrıştırıcı yapılabilir
      Örneğin yalnızca sürüm, bağımlılıklar ve hash içeren bir yapı
    • Aslında bu tür meta veriler RubyGems ya da PyPI tarafından önceden ayrıştırılıp veritabanına kaydediliyor
      uv’nin hızlı olmasının sebeplerinden biri de bu — paketi indirmeden bağımlılık hesabı yapılabiliyor
  • Geçmişte gem kurulum yöntemini iyileştiren bir prototip videosu hazırlamıştım
    how_gems_should_be.mov

  • Ruby’nin fibers’ları (veya Async kütüphanesi) çoğu zaman abartılıyor
    Thread’lerde olduğu gibi bağlantı havuzu gibi daha üst seviye koordinasyon sorunları yine var
    Yine de IO ağırlıklı kurulum işlerini eşzamansız yürütmek anlamlı bir performans artışı sağlayabilir

    • Saf Ruby’de daha fazla performans çıkarmaya çalışsam muhtemelen şöyle yaklaşırdım:
      1. ayrıştırması hızlı bir indeks formatı kullanmak (ilgili gist)
      2. ilk indirmeyi thread’lerle yapmak
      3. arşiv açma ve post-install adımını fork ile ayırmak
  • Global önbelleğin tüm bundler örnekleri tarafından paylaşılması” fikri değerlendiriliyor
    Uzun vadede büyük fayda sağlayabilir gibi görünüyor ama gizli karmaşıklıklar olup olmadığı değerlendiriliyor
    İlgili issue: rubygems #7249

    • Tamamen basit değil ama diğer ekosistemlerdeki öncü örnekler incelenirse fazlasıyla mümkün
      Ruby bu sorunu ilk çözen taraf olmayacak, dolayısıyla artık bunun avantajından yararlanma zamanı
  • Optimizasyonun temel ilkesi basit: Hiçbir şey yapmamak en hızlısıdır

    • “Akıllı kod hızlıdır” yanılgısını bırakmak gerekiyor
      Gerçek optimizasyon, gereksiz işi baştan hiç yapmamaktır