Rust को बेंचमार्किंग के लिए विशेष दृष्टिकोण की आवश्यकता क्यों है?
\n\nRust अपनी गति के लिए प्रसिद्ध है, लेकिन सबसे तेज़ भाषा को भी खराब कार्यान्वयन से धीमा किया जा सकता है। क्या आपने कभी Rust में कोई एल्गोरिदम लिखा है, यह सोचते हुए: "यह बिजली की गति से चलेगा!", और फिर चलाकर समझ नहीं पाए कि माइक्रोसेकंड कहाँ खो गए? परिचित लगता है?
\n\nRust में बेंचमार्किंग केवल टाइमर चालू करना और समय मापना नहीं है। यह एक पूरा विज्ञान है: कंपाइलर ऑप्टिमाइज़ेशन, प्रोसेसर कैशिंग, और यह भी कि Rust मेमोरी का प्रबंधन कैसे करता है, इन सबको ध्यान में रखना होता है। इस लेख में हम Rust प्रोग्राम को सही तरीके से बेंचमार्क करने का तरीका समझेंगे, ताकि आपको ईमानदार आंकड़े मिलें, न कि यादृच्छिक परिणाम।
\n\nहम std::time के साथ सरल माप से लेकर पेशेवर टूल criterion तक का सफर तय करेंगे, जिसका उपयोग वास्तविक प्रोजेक्ट्स में किया जाता है। चलिए शुरू करते हैं!
1. सबसे आसान तरीका: std::time::Instant
\n\nयदि आप कोड के एक टुकड़े के प्रदर्शन को जल्दी से "परखना" चाहते हैं - बिना कोई क्रेट इंस्टॉल किए, तो बस main में ही माप डालें। लेकिन एक चाल है: Rust ऑप्टिमाइज़र आपके कोड को "हटा" सकता है यदि वह परिणाम का उपयोग नहीं करता है।
\n\nuse std::time::Instant;\n\nfn main() {\n let start = Instant::now();\n\n // आपका कोड जिसे मापना है\n let mut sum = 0u64;\n for i in 0..1_000_000 {\n sum += i;\n }\n\n let duration = start.elapsed();\n println!("निष्पादन समय: {:?}", duration);\n // परिणाम का उपयोग करना अनिवार्य है!\n println!("योग: {}", sum);\n}\n\n\nकेवल एक बार चलाकर माप क्यों नहीं लेना चाहिए?
\nएक बार चलाना लॉटरी है। सिस्टम व्यस्त हो सकता है, प्रोसेसर थ्रॉटल कर रहा हो, कैश ठंडा हो। Rust में सही बेंचमार्क को यह करना चाहिए:
\n- \n
- कोड को वार्म-अप करना (warm-up) - माप से पहले कुछ पुनरावृत्तियाँ \n
- कोड को कई बार चलाना (हजारों पुनरावृत्तियाँ) \n
- परिणामों का औसत निकालना \n
इसे मैन्युअल रूप से लागू करना त्रुटियों का रास्ता है। इसलिए Rust समुदाय ने criterion बनाया।
2. पेशेवर बेंचमार्किंग: Criterion क्रेट
\n\nCriterion.rs Rust में बेंचमार्किंग के लिए डी-फैक्टो मानक है। इसका उपयोग स्वयं rustc कंपाइलर और सैकड़ों क्रेट्स में किया जाता है। यह क्यों बेहतरीन है?
\n- \n
- स्वचालित वार्म-अप और सांख्यिकीय प्रसंस्करण \n
- ग्राफ़ और पिछले रनों से तुलना \n
- रीग्रेशन का पता लगाना (यदि कोड धीमा हो जाता है - आपको पता चल जाएगा) \n
- पैरामीटराइज़्ड बेंचमार्क के लिए समर्थन \n
इंस्टॉलेशन और सेटअप
\nCargo.toml में जोड़ें:
[dev-dependencies]\ncriterion = { version = "0.5", features = ["html_reports"] }\n\n[[bench]]\nname = "my_benchmark"\nharness = false\n\n\nफ़ाइल benches/my_benchmark.rs बनाएँ:
use criterion::{black_box, criterion_group, criterion_main, Criterion};\n\n// वह फ़ंक्शन जिसे बेंचमार्क करना है\nfn fibonacci(n: u64) -> u64 {\n match n {\n 0 => 0,\n 1 => 1,\n _ => fibonacci(n - 1) + fibonacci(n - 2),\n }\n}\n\nfn bench_fibonacci(c: &mut Criterion) {\n c.bench_function("fibonacci 20", |b| {\n // black_box — ऑप्टिमाइज़र को कोड हटाने से रोकता है\n b.iter(|| fibonacci(black_box(20)))\n });\n}\n\ncriterion_group!(benches, bench_fibonacci);\ncriterion_main!(benches);\n\n\nचलाएँ: cargo bench। आप कुछ इस तरह देखेंगे:
fibonacci 20 time: [24.567 µs 24.789 µs 25.012 µs]\n\n\nतीन संख्याएँ विश्वास अंतराल हैं (निचली, मध्य, ऊपरी सीमा)। यदि अंतराल संकीर्ण है - बेंचमार्क स्थिर है।
\n\n3. गुप्त सामग्री: black_box
\n\nआपने उदाहरण में black_box देखा होगा। यह criterion का एक जादुई फ़ंक्शन है जो LLVM को तर्कों या परिणाम को ऑप्टिमाइज़ करने से रोकता है। इसके बिना, आपका बेंचमार्क 0 ns दिखा सकता है - क्योंकि कंपाइलर ने कंपाइल समय पर ही कॉन्स्टेंट की गणना कर ली।
उदाहरण प