Cargo के साथ कई बाइनरी कैसे बनाएं और प्रदर्शन न खोएं?
\n\nक्या आपने कभी ऐसी स्थिति का सामना किया है जब आपका Rust प्रोजेक्ट इतना बड़ा हो जाता है कि एक बाइनरी फ़ाइल पर्याप्त नहीं रह जाती? हो सकता है कि आप एक CLI उपयोगिता लिख रहे हों जिसमें विभिन्न कार्यों के लिए अलग-अलग निष्पादन योग्य फ़ाइलें होनी चाहिए: एक डेटा विश्लेषण के लिए, दूसरा रिपोर्ट जनरेशन के लिए, तीसरा API के साथ इंटरैक्शन के लिए। या आप एक सर्वर एप्लिकेशन बना रहे हैं जहाँ मॉनिटरिंग, अनुरोध प्रोसेसिंग और बैकग्राउंड कार्यों को अलग करना आवश्यक है।
\n\nप्रश्न "How can I build multiple binaries with Cargo?" Rust डेवलपर्स के बीच Stack Overflow पर सबसे लोकप्रिय प्रश्नों में से एक है। और यह कोई संयोग नहीं है। जब कोई प्रोजेक्ट सरल "Hello, World!" से आगे बढ़ता है, तो कई बाइनरी के निर्माण को सही ढंग से व्यवस्थित करने की क्षमता एक महत्वपूर्ण कौशल बन जाती है। लेकिन प्रदर्शन खोए बिना यह कैसे करें? कोड दोहराव में कैसे न फंसें और कंपाइलेशन को धीमा कैसे न करें?
\n\nइस लेख में हम केवल इस प्रश्न का उत्तर नहीं देंगे - हम दिखाएंगे कि कैसे कई बाइनरी के निर्माण को सिरदर्द से एक शक्तिशाली ऑप्टिमाइज़ेशन टूल में बदला जाए। आप सर्वोत्तम प्रथाओं, प्रदर्शन और यहां तक कि Cargo की गुप्त तरकीबों के बारे में जानेंगे जिनका उपयोग अनुभवी Rust डेवलपर्स करते हैं।
\n\nकई बाइनरी क्यों? एक निष्पादन योग्य फ़ाइल की समस्या
\n\nकल्पना करें: आपने डेटा के साथ काम करने के लिए एक शानदार लाइब्रेरी लिखी है। फिर आपने CLI इंटरफ़ेस जोड़ा। फिर वेब सर्वर। फिर टास्क शेड्यूलर। सब कुछ एक ही प्रोजेक्ट में। परिणाम? एक मोनोलिथ जो:
\n\n- \n
- हमेशा कंपाइल होता है, भले ही आप CLI में केवल एक पंक्ति बदल रहे हों। \n
- एक घटक को अपडेट करने पर पूरे एप्लिकेशन को पुनरारंभ करने की आवश्यकता होती है। \n
- परीक्षण और तैनाती में कठिन होता है। \n
समाधान - प्रोजेक्ट को कई बाइनरी में विभाजित करना। लेकिन Rust में इसे सही तरीके से कैसे करें?
\n\nमूल बातें: Cargo.toml और [[bin]] सेक्शन
\n\nसबसे आसान तरीका है अपने Cargo.toml में कई बाइनरी लक्ष्य घोषित करना। आइए एक बुनियादी उदाहरण देखें।
// Cargo.toml\n[package]\nname = "my_project"\nversion = "0.1.0"\nedition = "2021"\n\n[[bin]]\nname = "analyzer"\npath = "src/bin/analyzer.rs"\n\n[[bin]]\nname = "reporter"\npath = "src/bin/reporter.rs"\n\n[[bin]]\nname = "server"\npath = "src/bin/server.rs"\n\n[dependencies]\nserde = { version = "1.0", features = ["derive"] }\ntokio = { version = "1", features = ["full"] }\n\n\nअब src/bin/ फ़ोल्डर में आपके पास तीन फ़ाइलें हैं: analyzer.rs, reporter.rs और server.rs। उनमें से प्रत्येक अपने स्वयं के main() फ़ंक्शन के साथ एक पूर्ण बाइनरी है।
सब कुछ एक साथ बनाने के लिए:
\ncargo build --release\n\nया एक विशिष्ट बाइनरी:
\ncargo build --release --bin analyzer\n\nसरल? हाँ। लेकिन इसमें एक चाल है। यदि आप बिल्ड को ऑप्टिमाइज़ नहीं करते हैं, तो प्रत्येक बाइनरी फिर से कंपाइल होगी, भले ही वे समान निर्भरताओं का उपयोग करते हों। और यह प्रदर्शन खोने का सीधा रास्ता है।
\n\nबिल्ड प्रदर्शन ऑप्टिमाइज़ेशन: Cargo प्रोफाइल
\n\nकई बाइनरी के कुशल निर्माण का रहस्य Cargo प्रोफाइल में छिपा है। यह एक शक्तिशाली उपकरण है जो आपको विभिन्न परिदृश्यों के लिए कंपाइलेशन पैरामीटर सेट करने की अनुमति देता है।
\n\nबुनियादी प्रोफाइल: dev और release
\n\nडिफ़ॉल्ट रूप से, Cargo दो प्रोफाइल का उपयोग करता है: dev (विकास के लिए) और release (प्रोडक्शन के लिए)। लेकिन आप अपने स्वयं के प्रोफाइल बना सकते हैं या मौजूदा को संशोधित कर सकते हैं।
// Cargo.toml\n[profile.release]\nopt-level = 3 # अधिकतम कोड ऑप्टिमाइज़ेशन\nlto = "fat" # लिंक-टाइम ऑप्टिमाइज़ेशन (सभी क्रेट का विलय)\ncodegen-units = 1 # बेहतर ऑप्टिमाइज़ेशन के लिए एक कोडजेन थ्रेड\nstrip = true # डीबग जानकारी हटाना\n\n\nये सेटिंग्स विशेष रूप से उन बाइनरी के लिए महत्वपूर्ण हैं जिन्हें तेज़ी से काम करना चाहिए। लेकिन एक बारीक बात है: जितना अधिक आक्रामक