1 puan yazan GN⁺ 2 시간 전 | 1 yorum | WhatsApp'ta paylaş
  • Rust’ın önde gelen rastgele sayı crate’i rand, gündelik işlemleri birden fazla trait’e dağıttığı için; daha küçük bir açık/uygulama yüzeyi ve tutarlı bir kullanım deneyimi sunan urandom geliştirildi
  • Yüksek seviye işlemler tek bir Random yapısında toplandı ve Rng trait’i mühürlenerek, rastgele üreteç desteğinden çok API keşfedilebilirliği ve iç optimizasyonlar önceliklendirildi
  • Yeni bir rastgele sayı algoritması eklemek yerine Xoshiro256’nın çıktı işlevleri kullanım amacına göre seçildi; 1.000 adet f64 üretim kıyaslamasında rand 0.10.2’ye göre yaklaşık %31 daha yüksek throughput elde edildi
  • Eşit dağılımlı tamsayı örnekleme, eşik değerini gecikmeli hesaplayan tek bir önyargısız uygulama ile yeniden kullanım ve tek seferlik yolları birleştirdi; 500..20_000 aralığı kıyaslamasında rand’in iki yolundan da daha hızlıydı
  • Açık seed ile ham çıktı, desteklenen mimariler ve SemVer uyumlu sürümler boyunca yeniden üretilebilirlik sağlar; ancak özel rastgele üreteç bağlama ile rand’in geniş dağılım ve üçüncü taraf entegrasyon ekosisteminden vazgeçilir

Tek yerde toplanmış Random API’si

  • rand’in faydalı işlemleri birden fazla trait’e dağılmış durumda
    • Rastgele aralık üretimi için RngExt, dizilerden seçim için IndexedRandom, karıştırma için SliceRandom gerekir
    • rand 0.10, tek seferlik çağrılar için rand::random_range gibi kök seviye yardımcılar sunar
    • Ancak bir RNG handle’ını elde tutmak ya da seçim/karıştırma gibi dizi işlemlerini kullanmak için hâlâ birden fazla trait’in metotlarını bulmanız gerekir
  • Prelude ile import sayısını azaltsanız bile, genişletme metotlarının RNG, slice veya iterator tiplerinden hangisine uygulandığını bilmeniz gerekir; bu yüzden yalnızca IDE otomatik tamamlama ile bulmak zordur
  • urandom, yüksek seviye tüketici API’sini tek bir Random sarmalayıcı yapısı içinde toplar
    • urandom::new() ile Random<urandom::rng::Xoshiro256Rng> oluşturulur
    • uniform, choose, shuffle aynı nesne üzerinden çağrılabilir
    • Otomatik tamamlamada random, uniform, chance, choose, shuffle, sample gibi seçenekler görülebilir
    • Bunların hepsi özgün metotlardır; dolayısıyla yüksek seviye genişletme trait’lerini bulup import etmeniz gerekmez

Genişletilebilirlik yerine optimizasyonu seçen mühürlü Rng

  • rand, düşük seviye RNG trait’ini herkese açık bir genişletme noktası olarak ele alır; buna karşılık urandom’daki Rng trait’i mühürlüdür ve desteklenen üreteçler crate içinde seçilip uygulanır
    • İstediğiniz bir üreteci Random’a bağlayamazsınız
    • Yeni bir üreteç eklemek için urandom’ın kendisini değiştirmek gerekir
  • Amaç daha iyi bir algoritmaysa, Xoshiro256 ve ChaCha zaten bugün kendi rollerinde varsayılan tercih olarak yerleşmiş durumda ve öneriler de yavaş değişir
    • Daha iyi bir seçenek ortaya çıkarsa, gelecekteki bir major sürümde benimsenebilir
  • Başka projeler, programlama dilleri, eski algoritmalar, özel donanımlar veya yalnızca simülasyona yönelik üreteçlerle uyumluluk için yalnızca aynı üretece sahip olmak yetmez
    • Eşit dağılımlı örnekleme ve karıştırma gibi ilgili algoritmaların da aynı olması gerekir; bu nedenle tüm sözleşmeyi uygulayan özel bir implementasyon daha uygundur
  • Mühürlü trait sayesinde, bilinmeyen üreteçler ve istisna durumları için implementasyon sözleşmesi tasarlayıp belgelemeye gerek kalmadan urandom’ın ihtiyaç duyduğu ham işlemler eklenebilir
    • Üreteçler ve algoritmalar birbirine göre özelleştirilebilir; böylece rand’de mümkün olmayan bazı optimizasyonlar kullanılabilir
  • Çoğu uygulama için yeni bir PRNG implementasyonundan daha faydalı olan şey entropi seçimidir
    • Somut üreteçler, yerel from_seed kurucularını açık eder
    • ChaCha12Rng::from_seed(seed) gibi açık seed kullanan bir Random oluşturabilirsiniz
    • Keyfi RNG implementasyonları kabul edilmez, ama ileri düzey kullanıcıların ihtiyaç duyacağı düşünülen genişletme noktaları korunur

Aynı algoritmadan gelen performans artışı

  • urandom, yeni bir rastgele sayı üretim algoritması kullanmaz
    • 64 bit sistemlerde, kriptografik olmayan kullanım için urandom::new() ile rand::rngs::SmallRng aynı Xoshiro256 ailesini kullanır
    • Kriptografik kullanım için urandom::csprng() ile rand::rngs::StdRng, ChaCha12 kullanır
    • Yardımcı işlev rand::rng() de içeride ChaCha12 kullanır
  • rand’in üreteç arayüzü, tamsayı word’leri ve bayt doldurmayı sağlar; bu yüzden f64 gerektiren dağılımlar bile tam bir u64 ister
  • urandom::Rng, next_u32 ve next_u64’ün yanı sıra next_f32 ve next_f64 de sağlar
    • Kayan noktalı rastgele sayılar, tam bir word’den daha az rastgele bit gerektirir
    • Üreteçler bu metotları daha ucuz çıktı işlevleriyle override edebilir
  • Xoshiro implementasyonu, durum geçişini paylaşırken çıktı yollarını ayırır
    • u64 için Xoshiro256++ korunur
    • u32 ve kayan nokta için, üst bitleri bu kullanım amaçlarına uygun tasarlanmış daha hızlı Xoshiro256+ kullanılır
  • urandom 1.0 ve rand 0.10.2 ile sırasıyla 1.000 rastgele sayı üreten mikro kıyaslamaların sonuçları şöyleydi
    • Xoshiro u64: her iki tarafta da 814ns
    • Xoshiro u32: rand 836ns, urandom 788ns
    • Xoshiro f64: rand 1,033ns, urandom 788ns
    • ChaCha12 f64: rand 2,199ns, urandom 2,011ns
  • Xoshiro f64 için uçtan uca throughput yaklaşık %31 daha yüksek, çalışma süresi ise %24 daha kısaydı; ancak aynı işi yapan u64 yolu pratikte başa baştı
  • ChaCha12, next_f64’ü override etmediği için performans büyük ölçüde benzerdir
  • Kesin süreler makineye ve derleyiciye göre değişir; ayrıntılı koşullar için tam benchmark notlarına bakılabilir

Tekleştirilmiş eşit dağılımlı örnekleme yolu

  • Tamsayıları aralık uzunluğuna göre basitçe mod almak önyargı üretir; bu yüzden doğru eşit dağılımlı tamsayı örnekleme, üreteç çıktısının bir kısmını reddetmelidir
  • Doğru reddetme eşiğini hesaplamak, maliyetli bir mod işlemi gerektirir
    • Örnekleyici tekrar tekrar kullanılacaksa bu, başlangıç kurulum maliyeti olarak tolere edilebilir
    • Yalnızca tek bir değer üretildiğinde ise bu maliyet görece büyür
  • rand, bu farkı UniformSampler trait’i üzerinden açığa çıkarır
    • Oluşturulan UniformInt, eşiği önceden hesaplayarak önyargısız örnekleme yapar
    • Rng::random_range, kurulum maliyetinden kaçınmak için ayrı sample_single veya sample_single_inclusive hook’larını kullanır
    • Varsayılan özelliklerde tek seferlik kısa yol, biraz önyargılı ikinci bir algoritma kullanır
    • İsteğe bağlı unbiased özelliği bunu daha karmaşık yinelemeli bir sürümle değiştirir
  • urandom, eşiği gecikmeli hesaplayarak hem yeniden kullanım hem de tek seferlik aralıklar için tek bir önyargısız çarpma-reddetme implementasyonu kullanır
    • Daniel Lemire’in 2018 tarihli Fast Random Integer Generation in an Interval makalesinde anlatılan yaklaşımı izler
    • Pratikteki çoğu aralıkta ilk aday, bölme işleminden önce döndürülür
    • İlk aday döndürülemiyorsa, doğru eşik hesaplanır ve ardından önyargısız biçimde yineleme yapılır
    • Tüm aralığın istendiği range == 0 istisnası da ele alınır
  • Ayrı bir metot, ikinci bir algoritma, ön kurulum maliyeti veya önyargılı hızlı yol olmadan; aynı implementasyon hem yeniden kullanılan dağılımları hem de tek seferlik aralıkları işler
  • 500..20_000 aralığında 1.000 örnek çeken benchmark sonuçları şöyleydi
    • Yeniden kullanılan UniformInt: rand 1,098ns, urandom 950ns
    • Tek seferlik aralık: rand 1,079ns, urandom 942ns
  • rand sonuçları varsayılan özelliklere göredir; bu yüzden daha hızlı tek seferlik satır hafif önyargılı yolu kullanırken, urandom önyargısız durumda her iki yoldan da daha hızlıydı

Sürümler ve mimariler arasında yeniden üretilebilirlik

  • urandom, yeniden üretilebilirliği kamusal sözleşmenin bir parçası olarak görür
    • Aynı açık seed ve aynı düşük seviye RNG çağrı sırası verildiğinde, deterministik üreteçlerin ham çıktısı korunur
    • Desteklenen mimariler ve SemVer uyumlu sürümler boyunca kararlılık garanti edilir
    • 64 bit bir sunucu ile 32 bit WebAssembly istemcisi, replay için aynı üreteç tabanını kullanabilir
  • Bu uyumluluğu korumak için 32 bit mimarilerde performanstan ödün verilir
  • Bu, rand’in yeniden üretilebilirlik politikasından daha güçlü bir garantidir
    • rand’in taşınabilir üreteçleri ve örnekleme algoritmaları, minor sürümlerde farklı çıktı verebilir
    • SmallRng ve StdRng açıkça taşınabilir değildir; platforma veya kütüphane sürümüne göre de değişebilir

Tercihin bedeli ve hangi durumda uygun olduğu

  • urandom, genel işlemleri Random içinde toplayarak genişletme trait’leri olmadan kolayca bulunabilir hâle getirir
  • Üreteçler ve dağılımlar birlikte tasarlanarak daha ucuz Xoshiro çıktı yolları ve tek bir önyargısız eşit dağılımlı örnekleme yolu uygulanır
  • Açık seed ile başlatılan üreteçlerin kararlı ham akışı, deterministik oyunlar ve simülasyonlar için kullanılabilir
  • Buna karşılık, keyfi üreteçler içe aktarılamaz; ayrıca rand’in sunduğu daha geniş dağılım listesi ve üçüncü taraf entegrasyon ekosistemi de yoktur
  • Geniş bir ekosistem gerekiyorsa rand daha uygundur; daha küçük API yüzeyi, keşfedilebilirlik, bütünleşik optimizasyonlar ve güçlü yeniden üretilebilirlik politikasını tercih ediyorsanız urandom seçilebilir
  • Pakete crates.io, API belgeleri ve GitHub kaynak kodu üzerinden ulaşılabilir

1 yorum

 
GN⁺ 2 시간 전
Lobste.rs yorumları
  • randi fork etmek için yeterince neden var, ancak urandom adı /dev/urandom ile ilgili bir kütüphane gibi duyuluyor

    • Yararlı görünüyor, fakat adı kafa karıştırabilir. Yazıyı okumadan yalnızca adına baksaydım, dosya G/Ç’sine bağımlı olduğunu düşünerek incelemezdim
  • Sorunun farkındalığına katılıyorum, ama pub fn new() -> Random<impl Rng + Clone> hoşuma gitmiyor
    Tüm uygulamayı Random<T> where T: Rng ile parametrelemek zahmetli işleri artırır; derleme süresi ve dyn ile ilgili sorunlar ciddileşir. Bunun yerine struct Randomın somut bir tipe sahip olmasını, ya da ikinci seçenek olarak struct Random<T = rng::Xoshiro256Rng>ı tercih ederdim

  • Benzer bir sıkıntı yüzünden ben de daha önce kendim bir şey yapmıştım; ancak bu bir fork değil ve randden çok daha az işlev sunuyor

  • Benimle aynı sorunu hisseden birinin gerçekten çözüm üretmeye girişmesine sevindim. Rust’ta tuhaf biçimde trait çorbası kütüphaneleri yazdıran bir eğilim var gibi
    İşte kullandığım veritabanının çekirdek veri türlerinin en az 15 trait uygulaması gerekiyor; bu yüzden otomatik tamamlama berbat, dokümantasyon da kafa karıştırıcı. Trait sayısını biraz azalttık ama sık sık döngüsel bağımlılıklar ya da çekirdek testleri yazamama gibi sorunlara takılıyoruz

    • Bu, Java kökenli mimari astronotların aynı nesne yönelimli tarzı Rust’a uygulamasından kaynaklanan bir durum. Döngüsel bağımlılık, henüz ayrılamayan tek bir şeyi zorla böldüğünüzün ya da üç şeyi doğru ayırt edemediğinizin işaretidir. Kodun tamamını kontrol edebiliyorsanız trait yerine enum kullanabilirsiniz
    • Rust kriptografi ekosisteminde trait çorbası sorunu özellikle ağır; insanı çıldırtıyor
  • Bu kütüphane bana hem APOSD’nin derin arayüzlerini hem de Filippo’nun hata yapmayı zorlaştıracak şekilde tasarlanmış kriptografi çalışmalarını hatırlatıyor; ikisi de büyük övgü

    • Ancak urandom::new() kriptografik olarak güvenli bir rastgele sayı üreteci döndürmediği için tasarım tamamen hataya kapalı değil. Özellikle Linux’taki /dev/urandom güvenli olduğundan bu daha da kafa karıştırıcı
  • Bir başka rand alternatifi olarak basit ve hızlı bir rastgele sayı üreteci olan fastrand var. rand ve urandomdan daha basit, ama daha az özellik sunuyor