1 puan yazan GN⁺ 6 시간 전 | 1 yorum | WhatsApp'ta paylaş
  • Gleam formatlayıcısının özyinelemeli Document yapısında tekrarlanan ayrı ayrı heap tahsislerini arena tabanlı referanslarla değiştirerek 3 yıldır açık kalan performans iyileştirme sorunu çözüldü
  • Box<Self>, arena’da saklanan &Self ile değiştirildi ve Rust’ın ömür denetimi sayesinde arena kaldırıldıktan sonra iç verilerin referanslanmaması garanti altına alındı
  • Dil anahtar sözcükleri ve virgüller gibi tekrarlanan yüzlerce Document, yalnızca bir kez tahsis edilip yeniden kullanılarak pretty printer çalışma süresi 13 ms’den 9,8 ms’ye düştü; %24 hızlanma sağlandı
  • Kaynak okuma ve ayrıştırmayı da içeren toplam gleam format çalışma süresi de %13 kısaldı; tepe bellek kullanımı 8,4 MB’den 7,6 MB’ye inerek yaklaşık %10 azaldı
  • Arena’yı iletmek için fonksiyonların ve çağrı noktalarının geniş ölçekte değiştirilmesi gerektiğinden +2963/-1032 büyüklüğünde manuel çalışma yapıldı; ancak tekrarlayan ayrı tahsisleri azaltma yaklaşımıyla hem hız hem de bellek kullanımı iyileştirilebildi

Özyinelemeli Document yapısında ortaya çıkan tahsis maliyeti

  • Gleam’in pretty printer’ı, kodun nasıl yazdırılacağını ve satır sınırı aşıldığında nereden bölüneceğini temsil eden özyinelemeli bir Document veri yapısı kullanır
  • Başlıca varyantlar, yazdırılacak metni taşıyan String, satır kırılmasından önceki ve sonraki metni tanımlayan Break, birden fazla belgeyi gruplayan Group, satır kırıldığında girintiyi artıran Nest gibi yapılardan oluşur
  • [1, 2] listesi de String, Break, Nest, Group bileşimiyle ifade edilir
    • Geçerli satıra sığıyorsa [1, 2] gibi tek satırda yazdırılır
    • Satır sınırını aşarsa belirtilen Break’lere göre birden çok satıra bölünür, girintilenir ve sondaki virgül de yazdırılır
  • Nest gibi içinde başka bir Document taşıyan varyantlar daha önce Box<Self> kullandığından, her iç içe belge için heap üzerinde ayrı tahsis yapılması gerekiyordu; bu işlem çalışma süresinin önemli bir bölümünü oluşturabiliyordu
  • Gleam pretty printer’ının temel algoritması Strictly Pretty makalesinde görülebilir

Box’ı arena referansıyla değiştirmek

  • Mevcut Nest(Box<Self>), Nest(&'doc Self) olarak değiştirilerek yapının arena’da saklanan başka bir Document’ı referanslaması sağlandı
  • Referans tutabilmek için Document’a, string ömrü 'string’den ayrı olarak belge verisinin ömrü 'doc eklendi
  • Uygulamada typed_arena crate’i kullanıldı
    • alloc ile bir değer arena’ya kaydedildiğinde o değerin referansı döndürülür
    • Tahsis edilen veri, arena hayatta olduğu sürece kullanılabilir
    • Arena kapsam dışına çıktığında içindeki veriler de birlikte kaldırılır
  • alloc(&self, value: T) -> &mut T tarafından döndürülen referans arena’dan daha uzun yaşayamaz; Rust’ın borrow checker’ı arena kaldırıldıktan sonra geçersiz referansları engeller

Tekrarlanan belgeleri bir kez tahsis edip yeniden kullanmak

  • Arena yalnızca başka belgeleri referanslamak için değil, kod genelinde tekrarlanan Document’ları önbelleğe almak için de kullanıldı
  • String("fn"), String("pub"), String("type") gibi dil anahtar sözcüklerini temsil eden belgelerin her seferinde yeniden oluşturulmasına gerek kalmadı
  • Liste öğeleri arasında kullanılan Break { unbroken: ", ", broken: "," } gibi belgeler de bir kez tahsis edilip yeniden kullanıldı
  • Daha önce sürekli Box içine konması gereken yüzlerce küçük belge artık her biri yalnızca bir kez tahsis edilebilir hale geldi

Basit API ve geniş kapsamlı çağrı noktası değişiklikleri

  • Arena API’sinin kendisi basit olsa da mevcut kodun büyük bir bölümünü dönüştürme sürecinde tekrarlayan değişiklikler gerekti
  • Daha önce yalnızca Box::new çağıran fonksiyonların artık veriyi tahsis edecek ek bir arena argümanı alması gerekiyor
  • format_list gibi fonksiyonlar, liste öğelerini formatlayan alt fonksiyonlara da aynı arena’yı iletecek ve iç içe belgeleri Box::new yerine arena.alloc ile oluşturacak şekilde değiştirildi
  • Birkaç gün süren dikkatli arama-değiştirme sonunda çalışma +2963/-1032 büyüklüğünde bir pull request ile tamamlandı
  • Bu tekrarlayan ve manuel bir çalışmaydı; süreçte LLM kullanılmadı

Hız ve tepe bellek kullanımı iyileştirmeleri

  • Gerçek bir Gleam projesi olan squirrel formatlanırken pretty printer çalışma süresi 13 ms’den 9,8 ms’ye indi
    • Yalnızca pretty printer karşılaştırıldığında bu %24 performans artışı anlamına geliyor
  • gleam format, formatlamanın yanı sıra proje kaynak kodunu okuma ve ayrıştırma da yaptığından pretty printer toplam işin yalnızca bir bölümünü oluşturuyor; yine de toplam çalışma süresi %13 kısaldı
  • Tepe bellek kullanımı 8,4 MB’den 7,6 MB’ye düşerek yaklaşık %10 azaldı
  • Arena ile ayrı heap tahsislerinin sayısı azaltılınca formatlayıcı daha hızlı çalıştı ve bellek kullanımı da birlikte düştü

1 yorum

 
GN⁺ 6 시간 전
Lobste.rs yorumları
  • En yüksek bellek kullanımını %10 daha azaltmak istiyorsanız, referanslar yerine opak handle döndüren başka bir arena düşünmeye değer. Handle'ları 64 bit yerine 16 bit veya 32 bit yapabildiğiniz için, çok sayıda küçük tahsis olduğunda etkisi büyük olur.
    https://donsz.nl/blog/arenas/ adresine göre bunu destekleyen tek crate compact_arena; ancak ihtiyaçlarınıza uymuyorsa kendiniz uygulamak da zor değil.

    • Opak handle'lar kullanınca veriyi daha yoğun biçimde de saklayabilirsiniz. Kişisel bir proje olan columnar, Option<T> listelerini öğe başına yaklaşık 1 bitlik varlık bilgisi ve gerçekten var olan öğelerin T değerleri olarak ikiye ayırıp saklıyor.
      Bunun karşılığında referans türü olarak &Option<T> değil Option<&T> döndürüyor; daha genel olarak da yapısal özyinelemenin içine referans koymak yerine, referansları izleyerek yapısal olarak özyinelenen bir yaklaşım kullanıyor.
    • Söz konusu yazı 2 yıl önceki bir araştırmaya dayanıyordu ve tüm crate'leri eksiksiz ele almamış olabilir; bir hata ya da atlanmış yeni bir kütüphane bulursanız PR gönderebilirsiniz.
      Arenalar daha yaygın kullanılmayı hak ediyor; handle veya akıllı işaretçi yerine referans döndürmek, Rust'ta özyinelemeli enum desen eşlemeyi çok kullanışlı hâle getiriyor. Normalde if let MyAst::Add(MyAst::Multiply(a, b), c) = expr {} gibi yazmaya çalışsanız da aradaki box veya pointer yüzünden bu mümkün olmaz; ancak referanslar, nightly özellikler olmadan şeffaf biçimde desen eşleştirilebilir. Tüm referanslar tek bir arenayı işaret ediyorsa, ağacın tamamını birbirine bağlayan lifetime da tutarlı kalır.
    • Sonuçlar iyi olduğu için derleyicinin diğer bölümlerini de arena yaklaşımına geçirmeyi değerlendiriyorlar; bu yaklaşımı incelemeyi sürdürecekler.
  • Bu yöntem, bir başka Wadler tarzı pretty-printing kütüphanesi olan pretty'nin desteklediği yaklaşıma oldukça benziyor. Ancak birden fazla bellek tahsis yöntemini desteklediği için API'si biraz ağır gelebilir.

  • Yazıda alıntılanan makalenin yazarının Brunswick'te sıradan bir BT şirketinde çalışıyor olması şaşırtıcı.

    • Açık kaynakın güzelliği de bu. Uzmanlık ile geçimini sağladığı iş çoğu zaman pek bağlantılı olmayabiliyor.