प्रॉम्प्ट और स्कोप
एक C++ सर्विस प्रति रिक्वेस्ट कई छोटे, अल्पकालिक (short-lived) ऑब्जेक्ट्स बनाती है और उन्हें एक साथ नष्ट (destroy) करती है। वर्तमान कार्यान्वयन new और delete को बार-बार कॉल करता है, जिससे लेटेंसी में उतार-चढ़ाव (jitter) आता है। std::pmr::monotonic_buffer_resource, डिफ़ॉल्ट एलोकेटर और पूल रिसोर्स की तुलना करें; मुख्य कोड दिखाएं और बताएं कि यह विकल्प कब असुरक्षित होता है।
यह एलोकेटर सिमेंटिक्स, ऑब्जेक्ट लाइफटाइम और मापनीय ट्रेड-ऑफ़ पर आधारित एक कोडिंग प्रश्न है। मान लें कि ऑब्जेक्ट्स का उपयोग एक ही रिक्वेस्ट थ्रेड द्वारा किया जाता है और रिक्वेस्ट समाप्त होने पर रिसोर्स को नष्ट किया जा सकता है।
इंटरव्यूअर क्या मूल्यांकन करता है
- क्या आप रनटाइम-पॉलीमॉर्फिक
memory_resourceबाउंड्री और कंटेनर प्रकारों की व्याख्या कर सकते हैं। - क्या आप समझते हैं कि एक मोनोटोनिक रिसोर्स बढ़ता जाता है और आमतौर पर व्यक्तिगत ऑब्जेक्ट्स को पुनः प्राप्त (reclaim) नहीं करता है।
- क्या आप रिसोर्स के लाइफटाइम को पूरी प्रोसेस के बजाय किसी रिक्वेस्ट से बांधते हैं।
- क्या आप डैंगलिंग रेफरेंस, समय से पहले विनाश (early destruction), क्रॉस-थ्रेड उपयोग और एक्सेप्शन पाथ्स को पकड़ते हैं।
- क्या बेंचमार्क एलोकेशन, टेल लेटेंसी और पीक मेमोरी के माध्यम से लाभ साबित करते हैं।
उत्तर देने से पहले स्पष्ट करने वाले प्रश्न
- क्या सभी ऑब्जेक्ट्स रिक्वेस्ट के साथ समाप्त हो जाते हैं? लंबे समय तक चलने वाले ऑब्जेक्ट्स के लिए एक अलग रिसोर्स की आवश्यकता होती है।
- क्या व्यक्तिगत रिक्लेमेशन, फ्री-ब्लॉक रीयूज़, या हार्ड मेमोरी कैप की आवश्यकता है? एक पूल अधिक उपयुक्त हो सकता है।
- क्या कंटेनर और एलिमेंट्स एक ही
memory_resourceका उपयोग करते हैं? नेस्टेड स्ट्रिंग्स और एलोकेटर-अवेयर प्रकारों को इसे प्रोपेगेट करना चाहिए। - क्या कोई अन्य थ्रेड, एसिंक्रोनस टास्क, या कॉलर कंटेनर प्राप्त करेगा? यह सुरक्षित विनाश का निर्धारण करता है।
- क्या बॉटलनेक एलोकेशन कॉल्स, लॉक कंटेंशन, कैश लोकैलिटी, या कोई अन्य I/O/एल्गोरिदम लागत है?
30-सेकंड उत्तर फ्रेमवर्क
मैं पहले बैच लाइफटाइम की पुष्टि करता हूँ। यदि रिक्वेस्ट के अंत में सब कुछ डिस्कार्ड किया जा सकता है, तो एक मोनोटोनिक रिसोर्स एक प्रारंभिक बफर से शुरू हो सकता है, अपस्ट्रीम रिसोर्स से बड़े ब्लॉक प्राप्त कर सकता है, और नष्ट होने पर उन्हें एक साथ रिलीज़ कर सकता है; यह अल्पकालिक, अपेंड-जैसे एलोकेशन के लिए उपयुक्त है। यदि व्यक्तिगत रिक्लेमेशन या लंबे समय तक रीयूज़ की आवश्यकता है, तो मैं पूल या डिफ़ॉल्ट रिसोर्स चुनता हूँ। मैं रिसोर्स को रिक्वेस्ट स्कोप में रखता हूँ, पहले PMR यूज़र्स को नष्ट करता हूँ, और समान लोड के तहत लेटेंसी, एलोकेशन काउंट और पीक मेमोरी का बेंचमार्क करता हूँ।
स्टेप-बाय-स्टेप डीप डाइव
1. रिसोर्स और ऑब्जेक्ट लाइफटाइम बनाएं
monotonic_buffer_resource, memory_resource इंटरफ़ेस के माध्यम से एलोकेट करता है। यह कॉलर द्वारा प्रदान किए गए बफर से शुरू होता है और आवश्यकता पड़ने पर अपने अपस्ट्रीम रिसोर्स से अधिक ब्लॉक्स मांगता है। एक ऑब्जेक्ट को रिलीज़ करने से सामान्यतः स्टोरेज अपस्ट्रीम में वापस नहीं आता है; बल्क रिलीज़ विनाश के समय या स्पष्ट release() पर होता है। इसलिए रिसोर्स को इसका उपयोग करने वाले प्रत्येक कंटेनर और एलिमेंट से अधिक समय तक जीवित रहना चाहिए।
2. एलोकेशन पैटर्न का मिलान करें
रिक्वेस्ट पार्स ट्री, अस्थायी ASTs और सीरियलाइज़ेशन इंटरमीडिएट्स बैच-क्रिएट/बैच-डिस्ट्रॉय पैटर्न में फिट होते हैं। निश्चित आकार के ऑब्जेक्ट्स का बार-बार रिलीज़ और रीयूज़ unsynchronized_pool_resource की ओर संकेत करता है; थ्रेड्स के बीच साझा करने के लिए एक सिंक्रोनाइज़्ड डिज़ाइन या प्रति-थ्रेड रिसोर्सेज की आवश्यकता होती है। जब पैटर्न अस्थिर हो या ऑप्टिमाइज़ेशन के साक्ष्य कमजोर हों तो new_delete_resource अधिक सरल होता है।
3. नेस्टेड ऑब्जेक्ट्स में रिसोर्स को प्रोपेगेट करें
केवल बाहरी कंटेनर को PMR कंटेनर से बदलना पर्याप्त नहीं है। यदि किसी एलिमेंट में std::string, चाइल्ड कंटेनर, या एलोकेटर-अवेयर कंस्ट्रक्टर है, तो मैचिंग PMR प्रकार या यूज़ेस-एलोकेटर कंस्ट्रक्शन का उपयोग करें। अन्यथा आंतरिक ऑब्जेक्ट्स एक अलग रिसोर्स से एलोकेट कर सकते हैं और माप को अमान्य कर सकते हैं।
4. न्यूनतम कोड में स्वामित्व व्यक्त करें
#include <array>
#include <memory_resource>
#include <string>
#include <vector>
struct RequestArena {
std::array<std::byte, 64 * 1024> initial{};
std::pmr::monotonic_buffer_resource resource{initial.data(), initial.size()};
std::pmr::vector<std::pmr::string> names{&resource};
};
void handle_request() {
RequestArena arena;
arena.names.emplace_back("temporary", &arena.resource);
}प्रारंभिक बफर केवल अपस्ट्रीम एलोकेशन्स को कम करता है; कुल मेमोरी 64 KiB पर सीमित नहीं है। रिसोर्स अधिक ब्लॉक्स का अनुरोध कर सकता है, इसलिए प्रोडक्शन कोड को अपस्ट्रीम बाइट्स का निरीक्षण करना चाहिए, रिक्वेस्ट बजट लागू करना चाहिए, और एक्सेप्शन पर विनाश क्रम का परीक्षण करना चाहिए।
5. रिलीज़, एक्सेप्शन और रिटर्न वैल्यूज को संभालें
यदि किसी रिक्वेस्ट को बीच में रीसेट करना पड़े, तो कंटेनरों को साफ़ करें और release() को कॉल करें, फिर पुराने ऑब्जेक्ट्स के संदर्भों का उपयोग करना बंद कर दें। कभी भी ऐसा कंटेनर वापस न करें जिसका रिसोर्स स्कोप समाप्त हो रहा हो। सामान्य स्टैक अनवाइंडिंग वांछित क्रम प्रदान करता है: कंटेनर और एलिमेंट्स रिसोर्स से पहले नष्ट होते हैं। रिसोर्स के लाइफटाइम को डायनेमिक रूप से बढ़ाने से लीक और कन्करेंसी का जोखिम बढ़ जाता है।
6. बेंचमार्क के साथ निष्कर्ष निकालें
समान इनपुट, कंपाइलर विकल्प और थ्रेड सेटिंग्स के तहत, डिफ़ॉल्ट रिसोर्स, मोनोटोनिक और पूल की तुलना करें। प्रति रिक्वेस्ट एलोकेशन, P50/P99 लेटेंसी, पीक RSS, कुल अपस्ट्रीम बाइट्स, कैंसलेशन क्लीनअप समय और क्रॉस-रिक्वेस्ट रिटेंशन रिकॉर्ड करें। यदि लाइफटाइम बेमेल होने के कारण पीक बढ़ता है, तो एलोकेशन कॉल्स कम होने पर भी रिसोर्स को वापस पहले जैसा करें या विभाजित करें।
उच्च-गुणवत्ता वाला नमूना उत्तर
मैं पहले सत्यापित करता हूँ कि क्या ये सभी ऑब्जेक्ट्स रिक्वेस्ट के अंत में अमान्य हो जाते हैं। यदि ऐसा है, तो एक मोनोटोनिक रिसोर्स उपयुक्त है: यह एक प्रारंभिक बफर से शुरू होता है, आवश्यकतानुसार अपस्ट्रीम से ब्लॉक प्राप्त करता है, व्यक्तिगत रिक्लेमेशन को छोड़ देता है, और रिक्वेस्ट समाप्त होने पर बैच को रिलीज़ कर देता है। यह कई छोटे एलोकेशन और फ्री कॉल्स को कम करता है, लेकिन एक निश्चित मेमोरी कैप लागू नहीं करता है और उन ऑब्जेक्ट्स के लिए असुरक्षित है जिन्हें अलग से रिक्लेम किया जाना चाहिए।
मैं रिसोर्स को रिक्वेस्ट स्कोप में रखता हूँ, PMR कंटेनरों और एलोकेटर-अवेयर एलिमेंट्स को इसकी ओर इंगित करता हूँ, और रिसोर्स से पहले यूज़र्स को नष्ट करता हूँ। मैं निश्चित आकार के रीयूज़ के लिए पूल पर स्विच करता हूँ, थ्रेड्स के बीच सिंक्रोनाइज़्ड या प्रति-थ्रेड रिसोर्सेज का उपयोग करता हूँ, और साक्ष्य अपर्याप्त होने पर डिफ़ॉल्ट रिसोर्स बनाए रखता हूँ। रोलआउट से पहले मैं समान वर्कलोड के तहत P99, एलोकेशन काउंट, पीक मेमोरी और कैंसलेशन क्लीनअप की तुलना करता हूँ, जिसमें रिसोर्स एस्केप, एक्सेप्शन अनवाइंडिंग और हाई-वॉल्यूम रिक्वेस्ट शामिल हैं।
सामान्य गलतियां
मोनोटोनिक को एक स्वचालित मेमोरी कैप मानना
यह अपस्ट्रीम से ब्लॉक्स मांगना जारी रखता है। एक बजट निर्धारित करें, अपस्ट्रीम एलोकेशन्स का निरीक्षण करें, और बजट से अधिक होने पर काम को अस्वीकार करें या बैच में प्रोसेस करें।
रिसोर्स विनाश के बाद यूज़र्स को बनाए रखना
उनके आंतरिक पॉइंटर्स डैंगल (लटकते) रह जाते हैं। रिसोर्स ओनर को प्रत्येक यूज़र को घेरना चाहिए और उन्हें उस स्कोप से बाहर वापस करने से रोकना चाहिए।
केवल बाहरी कंटेनर को बदलना
नेस्टेड स्ट्रिंग्स अभी भी डिफ़ॉल्ट रिसोर्स का उपयोग कर सकती हैं। एलोकेटर-अवेयर कंस्ट्रक्शन और प्रत्येक नेस्टेड PMR प्रकार की जांच करें।
बिना बेंचमार्क के गति में सुधार का दावा करना
एलोकेटर परिवर्तन I/O या लॉक कंटेंशन द्वारा छिप सकते हैं। वर्कलोड को स्थिर रखें और लेटेंसी, पीक मेमोरी और एलोकेशन काउंट रिकॉर्ड करें।
फॉलो-अप्स और उनके उत्तर
फॉलो-अप 1: प्रत्येक एलिमेंट के लिए deallocate को कॉल क्यों नहीं किया जाता?
बल्क रिलीज़ ही डिज़ाइन का मुख्य ट्रेड-ऑफ़ है। प्रति-ऑब्जेक्ट रिक्लेमेशन सरल मोनोटोनिक मॉडल को विफल कर देगा; बारीक (fine-grained) रिलीज़ के लिए पूल या डिफ़ॉल्ट रिसोर्स का उपयोग करें।
फॉलो-अप 2: प्रारंभिक बफर कितना बड़ा होना चाहिए?
प्रोडक्शन वितरण से सामान्य रिक्वेस्ट आकारों का अनुमान लगाएं, एक्सेप्शन हेडरूम छोड़ें, और अपस्ट्रीम अनुरोधों का निरीक्षण करें। बहुत बड़ा होने पर स्टैक या रेजिडेंट मेमोरी बर्बाद होती है; बहुत छोटा होने पर अपस्ट्रीम एलोकेशन बढ़ जाता है।
फॉलो-अप 3: क्या एक मोनोटोनिक रिसोर्स को थ्रेड्स के बीच साझा किया जा सकता है?
बिना सुरक्षा के समवर्ती रूप से (concurrently) एक अनसिंक्रोनाइज़्ड रिसोर्स का उपयोग न करें। प्रति-थ्रेड/प्रति-रिक्वेस्ट रिसोर्सेज या सत्यापित लाइफटाइम वाले स्पष्ट रूप से सिंक्रोनाइज़्ड अपस्ट्रीम डिज़ाइन को प्राथमिकता दें।
फॉलो-अप 4: आप कैसे साबित करते हैं कि कोई क्रॉस-रिक्वेस्ट लीक नहीं है?
एक बार-बार रिक्वेस्ट क्रम चलाएं और प्रति-रिक्वेस्ट पीक, संचयी अपस्ट्रीम बाइट्स और RSS ट्रेंड की तुलना करें। कैंसलेशन, एक्सेप्शन और ओवरसाइज़्ड रिक्वेस्ट के बाद विनाश और रिकवरी की पुष्टि करें।