एक पैकेज — दो संस्थाएँ: मिथक या वास्तविकता?
\n\nकल्पना करें: आपने Rust में बड़े डेटा के साथ काम करने के लिए एक अद्भुत लाइब्रेरी लिखी है। यह बिजली की गति से काम करती है, सुरक्षित और सुरुचिपूर्ण है। लेकिन आपको न केवल अन्य डेवलपर्स के लिए API चाहिए, बल्कि एक तैयार CLI उपयोगिता भी चाहिए, ताकि लाइब्रेरी का परीक्षण किया जा सके या इसे पाइपलाइनों में उपयोग किया जा सके। यहाँ एक क्लासिक प्रश्न उठता है, जिसने Stack Overflow पर हज़ारों व्यूज़ प्राप्त किए हैं: «Package with both a library and a binary?» (पैकेज जिसमें लाइब्रेरी और निष्पादन योग्य फ़ाइल दोनों हों)।
\n\nRust में शुरुआत करने वाले के लिए यह एक पहेली की तरह लगता है। अनुभवी डेवलपर के लिए, यह प्रोजेक्ट संरचना और प्रदर्शन को अनुकूलित करने का कार्य है। इस लेख में, हम न केवल इस प्रश्न का उत्तर देंगे, बल्कि अनुकूलन और प्रदर्शन पर ध्यान केंद्रित करेंगे — दो चीज़ें जिनके लिए Rust को सबसे अधिक पसंद किया जाता है।
\n\nहम समझेंगे कि ऐसे पैकेज को सही तरीके से कैसे व्यवस्थित करें, कोड की नकल से कैसे बचें, संकलन को कैसे तेज़ करें और अपने बाइनरी को वास्तव में तेज़ कैसे बनाएं। चलिए शुरू करते हैं!
\n\nपैकेज संरचना: Cargo.toml सब कुछ तय करता है
\n\nRust में, एक Cargo पैकेज में लाइब्रेरी (lib) और कई बाइनरी (bin) दोनों हो सकते हैं। यह जादू नहीं है, बल्कि फ़ोल्डर संरचना के बारे में एक सरल समझौता है। आइए एक न्यूनतम उदाहरण देखें।
\n\nएक प्रोजेक्ट बनाएं:
\n\ncargo new my_tool --lib\n\nअब फ़ाइल Cargo.toml को बदलें ताकि स्पष्ट रूप से बताया जा सके कि हमारे पास लाइब्रेरी और बाइनरी दोनों हैं:
[package]\nname = "my_tool"\nversion = "0.1.0"\nedition = "2021"\n\n[lib]\nname = "my_tool_lib"\npath = "src/lib.rs"\n\n[[bin]]\nname = "my_tool"\npath = "src/main.rs"\n\n[dependencies]\n\n\nफ़ोल्डर संरचना इस प्रकार दिखेगी:
\n\nmy_tool/\n├── Cargo.toml\n├── src/\n│ ├── lib.rs // लाइब्रेरी\n│ └── main.rs // बाइनरी\n\n\nसब कुछ सरल है, है ना? लेकिन शैतान विवरण में छिपा है। यदि आप केवल लाइब्रेरी से कोड को main.rs में कॉपी करते हैं, तो आपको नकल और प्रदर्शन संबंधी समस्याएँ होंगी। समझदारी से काम करना होगा।
\n\nसही दृष्टिकोण: बाइनरी लाइब्रेरी का उपयोग करता है
\n\nबाइनरी को लाइब्रेरी को एक बाहरी क्रेट के रूप में आयात करना चाहिए। भले ही वे एक ही पैकेज में हों। इससे न केवल नकल कम होती है, बल्कि Rust कंपाइलर को क्रॉस-क्रेट ऑप्टिमाइज़ेशन (LTO) के माध्यम से कोड को बेहतर ढंग से अनुकूलित करने की अनुमति मिलती है।
\n\nउदाहरण src/lib.rs:
pub fn calculate(data: &[u64]) -> u64 {\n data.iter().sum()\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn test_calculate() {\n assert_eq!(calculate(&[1, 2, 3]), 6);\n }\n}\n\n\nउदाहरण src/main.rs:
use my_tool_lib::calculate;\n\nfn main() {\n let numbers = vec![10, 20, 30];\n println!("योग: {}", calculate(&numbers));\n}\n\n\nध्यान दें: हम my_tool_lib का उपयोग कर रहे हैं — यह वह नाम है जो हमने Cargo.toml में लाइब्रेरी के लिए निर्धारित किया था। कंपाइलर स्वचालित रूप से उन्हें एक बाइनरी में जोड़ देगा, लेकिन मॉड्यूलरिटी बनाए रखेगा।
प्रदर्शन अनुकूलन: LTO, इनलाइन और बेंचमार्क
\n\nअब जब संरचना तैयार है, तो मुख्य बात पर बात करते हैं — अपने पैकेज से अधिकतम कैसे प्राप्त करें। आखिरकार, यदि आप डेटा प्रोसेसिंग के लिए एक उपकरण बना रहे हैं, तो हर माइक्रोसेकंड मायने रखता है।
\n\n1. Link-Time Optimization (LTO) चालू करें
\n\nLTO कंपाइलर को क्रेट की सीमाओं के पार कोड को अनुकूलित करने की अनुमति देता है। LTO के बिना, लाइब्रेरी और बाइनरी के फ़ंक्शन अलग-अलग अनुकूलित होते हैं। LTO के साथ, वे एक इकाई के रूप में अनुकूलित होते हैं।
\n\nCargo.toml में जोड़ें:
[profile.release]\nlto = true\ncodegen-units = 1\n\n\nइससे संकलन का समय बढ़ेगा, लेकिन बाइनरी काफ़ी तेज़ हो जाएगी। शैक्षिक साइट pythonlib.ru के लिए यह जोर देना महत्वपूर्ण है: LTO Rust में प्रोडक्शन बिल्ड के लिए मानक है।
\n\n2. इनलाइन संकेतों का उपयोग करें
\n\nयदि लाइब्रेरी में महत्वपूर्ण फ़ंक्शन हैं, तो