1 पॉइंट द्वारा GN⁺ 2 시간 전 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • Rust की प्रमुख random crate rand में रोज़मर्रा के operations कई traits में बिखरे हुए हैं, इसलिए छोटे public और implementation surface तथा सुसंगत उपयोग अनुभव वाली urandom विकसित की गई
  • high-level operations को एक Random struct में जोड़ा गया और Rng trait को sealed किया गया, ताकि arbitrary generator support के बजाय API discoverability और internal optimization को प्राथमिकता दी जा सके
  • कोई नया random algorithm पेश किए बिना, Xoshiro256 के output function को use-case के हिसाब से चुनकर, 1,000 f64 generate करने वाले benchmark में rand 0.10.2 की तुलना में लगभग 31% अधिक throughput दर्ज किया गया
  • uniform integer sampling ने threshold को lazily compute करने वाले एक unbiased implementation से reusable और one-off paths को unified किया, और 500..20_000 range benchmark में यह rand के दोनों paths से तेज़ रहा
  • explicit seed के raw output के लिए supported architectures और SemVer-compatible releases में reproducibility की guarantee दी जाती है, लेकिन arbitrary generator plugging और rand के विशाल distribution व third-party integration ecosystem को छोड़ा गया

एकीकृत Random API

  • rand के उपयोगी operations कई traits में बिखरे हुए हैं
    • random range generation के लिए RngExt, sequence selection के लिए IndexedRandom, और shuffling के लिए SliceRandom चाहिए
    • rand 0.10 one-off calls के लिए rand::random_range जैसे root-level helpers देता है
    • RNG handle बनाए रखना हो या selection/shuffling जैसे sequence operations इस्तेमाल करने हों, तो अब भी कई traits के methods ढूंढने पड़ते हैं
  • prelude से imports कम हो भी जाएं, फिर भी यह जानना पड़ता है कि extension method RNG, slice या iterator में से किस type पर लागू होता है, इसलिए केवल IDE autocomplete से इसे ढूंढना मुश्किल है
  • urandom high-level consumer API को एक Random wrapper struct में रखता है
    • urandom::new() से Random<urandom::rng::Xoshiro256Rng> बनता है
    • uniform, choose, shuffle को उसी object पर call किया जा सकता है
    • autocomplete में random, uniform, chance, choose, shuffle, sample आदि दिखते हैं
    • ये सभी inherent methods हैं, इसलिए high-level extension traits ढूंढने या import करने की ज़रूरत नहीं है

extensibility के बजाय optimization चुनने वाला sealed Rng

  • rand low-level RNG traits को public extension point मानता है, लेकिन urandom का Rng trait sealed है, इसलिए supported generators crate के अंदर चुने और implement किए जाते हैं
    • arbitrary generator को Random से plug नहीं किया जा सकता
    • नया generator जोड़ने के लिए urandom खुद बदलना होगा
  • अगर लक्ष्य बेहतर algorithm है, तो अभी role-specific defaults के रूप में Xoshiro256 और ChaCha पहले से मौजूद हैं, और recommendations भी धीरे-धीरे बदलती हैं
    • बेहतर विकल्प आने पर भविष्य के major release में उसे अपनाया जा सकता है
  • दूसरे projects, programming languages, legacy algorithms, special hardware या simulation-specific generators के साथ compatibility के लिए केवल generator समान होना पर्याप्त नहीं है
    • uniform sampling और shuffling जैसे संबंधित algorithms भी समान होने चाहिए, इसलिए पूरे contract को implement करने वाला dedicated implementation ज़्यादा उपयुक्त है
  • sealed trait की वजह से unknown generators और edge cases के लिए implementation contract design/document किए बिना urandom में केवल आवश्यक raw operations जोड़े जा सकते हैं
    • generators और algorithms को एक-दूसरे के हिसाब से specialize किया जा सकता है, जिससे rand में उपलब्ध न होने वाली कुछ optimizations संभव होती हैं
  • ज़्यादातर applications में नए PRNG implementation की तुलना में entropy selection ज़्यादा उपयोगी है
    • concrete generators native from_seed constructors expose करते हैं
    • ChaCha12Rng::from_seed(seed) की तरह explicit seed वाला Random बनाया जा सकता है
    • arbitrary RNG implementation स्वीकार नहीं किया जाता, लेकिन advanced users को जिन extension points की ज़रूरत पड़ने की उम्मीद है, वे बनाए रखे गए हैं

उसी algorithm से मिला performance improvement

  • urandom कोई नया random generation algorithm इस्तेमाल नहीं करता
    • 64-bit systems पर non-cryptographic use के लिए urandom::new() और rand::rngs::SmallRng एक ही Xoshiro256 family का उपयोग करते हैं
    • cryptographic use के लिए urandom::csprng() और rand::rngs::StdRng ChaCha12 का उपयोग करते हैं
    • convenience function rand::rng() का internal generator भी ChaCha12 है
  • rand का generator interface integer words और byte filling देता है, इसलिए f64 चाहिए होने वाली distributions भी पूरा u64 मांगती हैं
  • urandom::Rng next_u32, next_u64 के साथ-साथ next_f32 और next_f64 भी देता है
    • floating-point random numbers को पूरे word से कम random bits चाहिए होते हैं
    • generators इन methods को सस्ते output function से override कर सकते हैं
  • Xoshiro implementation state transition share करता है, लेकिन output paths अलग रखता है
    • u64 के लिए Xoshiro256++ बनाए रखता है
    • u32 और floating-point के लिए तेज़ Xoshiro256+ इस्तेमाल करता है, जिसके upper bits इस use-case के लिए design किए गए हैं
  • urandom 1.0 और rand 0.10.2 से क्रमशः 1,000 random numbers generate करने वाले microbenchmark results इस प्रकार हैं
    • Xoshiro u64: दोनों 814ns
    • Xoshiro u32: rand 836ns, urandom 788ns
    • Xoshiro f64: rand 1,033ns, urandom 788ns
    • ChaCha12 f64: rand 2,199ns, urandom 2,011ns
  • Xoshiro f64 का end-to-end throughput लगभग 31% अधिक और execution time 24% कम था, लेकिन समान काम करने वाला u64 path व्यावहारिक रूप से बराबर रहा
  • ChaCha12 next_f64 को override नहीं करता, इसलिए performance broadly similar है
  • exact timings machine और compiler के हिसाब से बदल सकती हैं; विस्तृत conditions पूरे benchmark notes में देखी जा सकती हैं

एकीकृत uniform sampling path

  • integers को range length से simple modulo करने पर bias पैदा होता है, इसलिए सही uniform integer sampling को generator output के कुछ हिस्से reject करने पड़ते हैं
  • सही rejection threshold calculation में महंगा modulo operation चाहिए होता है
    • sampler को बार-बार इस्तेमाल करना हो तो initial setup cost स्वीकार्य हो सकती है
    • सिर्फ एक value generate करते समय यह cost अपेक्षाकृत बड़ी हो जाती है
  • rand इस अंतर को UniformSampler trait से expose करता है
    • generated UniformInt threshold को पहले से compute कर bias-free sampling करता है
    • Rng::random_range initial setup से बचने के लिए अलग sample_single या sample_single_inclusive hook इस्तेमाल करता है
    • default feature में one-off shortcut path थोड़ा biased दूसरा algorithm इस्तेमाल करता है
    • optional unbiased feature इसे ज़्यादा complex iterative version से बदलता है
  • urandom threshold को lazily compute करके reusable और one-off ranges दोनों के लिए एक ही unbiased multiply-and-reject implementation इस्तेमाल करता है
    • Daniel Lemire के 2018 paper Fast Random Integer Generation in an Interval में बताए गए तरीके का पालन करता है
    • ज़्यादातर practical ranges में पहला candidate division से पहले return हो जाता है
    • अगर पहला candidate return नहीं हो सकता, तो exact threshold compute करके बिना bias के iterate किया जाता है
    • पूरी range request होने पर range == 0 exception भी handle किया जाता है
  • अलग method, दूसरा algorithm, upfront setup cost या biased fast path के बिना वही implementation reusable distribution और one-off ranges संभालता है
  • 500..20_000 range से 1,000 samples निकालने वाले benchmark results इस प्रकार हैं
    • reusable UniformInt: rand 1,098ns, urandom 950ns
    • one-off range: rand 1,079ns, urandom 942ns
  • rand results default features पर आधारित हैं, इसलिए तेज़ one-off row भी थोड़ा biased path है, जबकि urandom unbiased रहते हुए दोनों paths से तेज़ था

releases और architectures में reproducibility

  • urandom reproducibility को public contract का हिस्सा मानता है
    • same explicit seed और same low-level RNG call order दिए जाने पर deterministic generator का raw output बना रहता है
    • supported architectures और SemVer-compatible releases में stability guarantee की जाती है
    • 64-bit server और 32-bit WebAssembly client replay के लिए same generator base इस्तेमाल कर सकते हैं
  • इस compatibility को बनाए रखने के लिए 32-bit architectures पर performance की बलि दी जाती है
  • यह rand की reproducibility policy से stronger guarantee है
    • rand के portable generators और sampling algorithms minor releases में output बदल सकते हैं
    • SmallRng और StdRng स्पष्ट रूप से portable नहीं हैं और platform या library release के अनुसार भी बदल सकते हैं

trade-offs और कब चुनें

  • urandom common operations को Random में इकट्ठा कर extension traits के बिना उन्हें आसानी से discoverable बनाता है
  • generator और distribution को साथ design करके सस्ता Xoshiro output path और एक unbiased uniform sampling path implement करता है
  • explicit seed वाले generators की stable raw stream deterministic games और simulations में काम आ सकती है
  • बदले में arbitrary generator import नहीं किए जा सकते, और rand की बड़ी distribution list व third-party integration ecosystem भी उपलब्ध नहीं है
  • अगर व्यापक ecosystem चाहिए तो rand उपयुक्त है; अगर छोटा API surface, discoverability, integrated optimization और strong reproducibility policy पसंद है तो urandom चुना जा सकता है
  • package crates.io, API docs, और GitHub source पर देखा जा सकता है

1 टिप्पणियां

 
GN⁺ 2 시간 전
Lobste.rs टिप्पणियाँ
  • rand को fork करने की पर्याप्त वजहें हैं, लेकिन urandom नाम सुनने में /dev/urandom से जुड़ी किसी लाइब्रेरी जैसा लगता है

    • यह उपयोगी लगता है, लेकिन नाम भ्रम पैदा कर सकता है। अगर मैंने लेख पढ़े बिना सिर्फ नाम देखा होता, तो शायद मैं समझता कि यह file I/O पर निर्भर है और इसे देखता ही नहीं
  • मैं समस्या-बोध से सहमत हूँ, लेकिन pub fn new() -> Random<impl Rng + Clone> पसंद नहीं आया
    अगर पूरे application को Random<T> where T: Rng के रूप में parameterize किया जाए, तो झंझट वाला काम बढ़ जाता है, और compile timedyn से जुड़ी समस्याएँ गंभीर हो जाती हैं। मैं बेहतर समझूँगा कि struct Random का कोई concrete type हो, या दूसरे विकल्प के रूप में struct Random<T = rng::Xoshiro256Rng> चुना जाए

  • ऐसी ही निराशा की वजह से मैं भी पहले खुद कुछ बना चुका हूँ, लेकिन वह fork नहीं है, और rand से बहुत कम features हैं

  • अच्छा लगा कि मेरी जैसी समस्या महसूस करने वाला कोई व्यक्ति वास्तव में उसे हल करने आगे आया। Rust में अजीब तरह से trait soup लाइब्रेरी बनवाने की प्रवृत्ति दिखती है
    काम में जिन databases से मैं निपटता हूँ, उनके core data types को कम-से-कम 15 traits implement करने पड़ते हैं, जिससे autocomplete बुरी तरह बिगड़ जाता है और documentation भी उलझनभरी हो जाती है। मैंने traits की संख्या कुछ कम की, लेकिन अक्सर circular dependencies या core tests न लिख पाने जैसी समस्याओं से अटक जाता हूँ

    • यह Java पृष्ठभूमि वाले architecture astronauts के उसी object-oriented style को Rust पर लागू करने का नतीजा है। Circular dependency इस बात का संकेत है कि या तो आपने ऐसी एक ही चीज़ को ज़बरदस्ती बाँट दिया है जिसे अभी अलग नहीं किया जा सकता, या फिर तीन अलग चीज़ों को ठीक से अलग नहीं पहचाना। अगर आप पूरे code को नियंत्रित कर सकते हैं, तो trait की जगह enum इस्तेमाल कर सकते हैं
    • Rust cryptography ecosystem में trait soup की समस्या खास तौर पर इतनी ज़्यादा है कि पागल कर दे
  • यह लाइब्रेरी मुझे APOSD के deep interfaces और Filippo के ऐसे cryptography work दोनों की याद दिलाती है, जिन्हें गलती करना मुश्किल बनाने के लिए डिज़ाइन किया गया है, और दोनों ही बड़ी तारीफ़ हैं

    • लेकिन urandom::new() cryptographically secure random number generator वापस नहीं करता, इसलिए यह पूरी तरह mistake-proof design नहीं है। खासकर इसलिए भी कि Linux का /dev/urandom सुरक्षित है, जिससे और भ्रम होता है
  • rand के एक और विकल्प के रूप में सरल और तेज़ random generator fastrand है। यह rand और urandom से अधिक सरल है, लेकिन features भी कम हैं