समस्या और लागू परिदृश्य
एक C++17 मेट्रिक्स कलेक्टर लगातार 8 एटॉमिक काउंटरों को स्टोर करता है। अलग-अलग फिजिकल कोर पर पिन किए गए आठ थ्रेड्स में से प्रत्येक एक अलग काउंटर पर 50 मिलियन रिलैक्स्ड इंक्रीमेंट करता है। मापे गए टार्गेट पर, प्रत्येक काउंटर 8 बाइट्स लेता है और ऑब्जेक्ट 64-बाइट बाउंड्री पर शुरू होता है, इसलिए सभी आठ काउंटर एक 64-बाइट कैश लाइन में आ जाते हैं। अंतिम कुल योग 400 मिलियन बिल्कुल सही है, लेकिन थ्रेड्स जोड़ने पर थ्रूपुट खराब होता जाता है। perf c2c या इसके समकक्ष प्रोफाइलर उस लाइन पर कई HITM इवेंट्स मैप करता है।
यह समस्या मल्टीकोर कैश कोहेरेंस, डेटा लेआउट, प्रदर्शन साक्ष्य और प्रयोग डिज़ाइन का परीक्षण करती है। यह C++, इन्फ्रास्ट्रक्चर, लो-लेटेंसी, डेटाबेस-कर्नेल और परफॉर्मेंस-इंजीनियरिंग भूमिकाओं पर लागू होती है। मुख्य कौशल भाषा, ऑपरेटिंग-सिस्टम और हार्डवेयर सीमाओं से परे है, इसलिए श्रेणी general है। एक रिलैक्स्ड ऑपरेशन मेमोरी-ऑर्डर बाधाओं को कमजोर करता है; यह एटॉमिक राइट के कारण होने वाले कोहेरेंस ट्रैफ़िक को नहीं हटाता है।
64 बाइट्स को इस टार्गेट की मापी गई विशेषता मानें, कोई सार्वभौमिक स्थिरांक नहीं। एक समाधान को कार्यान्वयन के डिस्ट्रक्टिव-इंटरफेरेंस साइज़ या समर्थित टार्गेट पर मान्य लेआउट को प्राथमिकता देनी चाहिए, जबकि तुलनीय पैक्ड और फिक्स्ड बेंचमार्क को बनाए रखना चाहिए।
साक्षात्कारकर्ता क्या मूल्यांकन करता है
पहला, क्या उम्मीदवार शुद्धता (correctness) को स्केलेबिलिटी से अलग कर सकता है? थ्रेड्स अलग-अलग एटॉमिक ऑब्जेक्ट्स पर लिखते हैं, इसलिए कोई अपडेट नष्ट नहीं होता है। प्रोसेसर कैश-लाइन ग्रैन्युलैरिटी पर कोहेरेंस बनाए रखता है, इसलिए स्वतंत्र एड्रेस अभी भी एक-दूसरे को अमान्य (invalidate) कर सकते हैं।
दूसरा, क्या उम्मीदवार राइट ओनरशिप (write ownership) की व्याख्या कर सकता है? इससे पहले कि कोई कोर लाइन में किसी भी काउंटर को संशोधित करे, उसे एक लिखने योग्य कॉपी की आवश्यकता होती है। जब कोई अन्य कोर उस लाइन में एक अलग काउंटर को संशोधित करता है, तो यह पिछले कोर की कॉपी को अमान्य कर देता है। लाइन कोरों के बीच घूमती रहती है और एप्लिकेशन डेटा निर्भरता से असंबंधित सीरियलाइज़ेशन बनाती है।
तीसरा, क्या उम्मीदवार एक साक्ष्य श्रृंखला (evidence chain) बना सकता है? एक मजबूत उत्तर "मल्टीथ्रेडिंग धीमी है" से सीधे फॉल्स शेयरिंग पर नहीं कूदता। यह एक और कई थ्रेड्स की तुलना करता है, कोरों को पिन करता है, एड्रेस और फ़ील्ड ऑफ़सेट मैप करता है, HITM हॉटस्पॉट्स का पता लगाता है, अलग किए गए लेआउट का अवलोकन करता है, और ट्रू शेयरिंग, लॉक्स, CPU माइग्रेशन, NUMA और मेमोरी बैंडविड्थ को खारिज करता है।
चौथा, क्या उम्मीदवार सबसे कम लागत वाला समाधान चुन सकता है? डिस्ट्रक्टिव-इंटरफेरेंस सीमाओं द्वारा हॉट राइटर्स को अलग करना लेआउट को ठीक करता है। यदि कुल योग केवल काम पूरा होने के बाद पढ़ा जाता है, तो थ्रेड-लोकल साधारण काउंटर और एक रिडक्शन बेहतर होते हैं क्योंकि वे अधिकांश शेयर्ड राइट्स को हटा देते हैं। लाइव-रीड की आवश्यकता इस विकल्प को बदल देती है।
पाँचवाँ, क्या उम्मीदवार स्पेस की सीमा बता सकता है? इस टार्गेट पर, 8-बाइट स्लॉट को 64 बाइट्स तक विस्तारित करने से दस लाख स्लॉट आठ गुना बड़े हो जाते हैं और कैश तथा TLB दबाव बढ़ सकता है। बिना माप के प्रत्येक फ़ील्ड में पैडिंग जोड़ना एक सही ऑप्टिमाइज़ेशन नहीं है।
उत्तर देने से पहले स्पष्ट करने योग्य प्रश्न
- क्या प्रत्येक काउंटर का वास्तव में एक ही एक्सक्लूसिव राइटर है? यदि कई थ्रेड्स एक ऑब्जेक्ट को अपडेट करते हैं, तो वह ट्रू शेयरिंग है; आसन्न फ़ील्ड्स को अलग करने से उसी ऑब्जेक्ट पर ओनरशिप कंटेंशन को दूर नहीं किया जा सकता है।
- रीड्स कितने ताज़ा (fresh) होने चाहिए?
joinके बाद ही पढ़े जाने वाले मान के लिए थ्रेड-लोकल साधारण इंटीजर का उपयोग किया जा सकता है। ऑनलाइन स्क्रैपिंग के लिए एटॉमिक शार्ड्स और रीड-टाइम योग की आवश्यकता हो सकती है। - क्या थ्रेड्स अलग-अलग फिजिकल कोर पर चलते हैं? एक ही कोर पर टाइम स्लाइसिंग, माइग्रेशन, ओवरसब्सक्रिप्शन या SMT परिणाम को बदल देता है। परीक्षण में पिनिंग करें और टोपोलॉजी रिकॉर्ड करें।
- टार्गेट का इंटरफेरेंस साइज़ और वास्तविक लेआउट क्या है? सोर्स कोड के क्रम पर भरोसा करने के बजाय कार्यान्वयन स्थिरांक,
sizeof,alignof, एरे स्ट्राइड और एड्रेस का निरीक्षण करें। - क्या HITM सैंपल अलग-अलग फ़ील्ड ऑफ़सेट पर मैप होते हैं? एक एड्रेस पर HITM ट्रू शेयरिंग का सुझाव देता है। एक लाइन में अलग-अलग ऑफ़सेट को छूने वाले विभिन्न राइटर्स फॉल्स शेयरिंग का समर्थन करते हैं।
30-सेकंड उत्तर ढाँचा
"सही परिणाम दिखाता है कि एटॉमिकिटी काम कर रही है; स्केलिंग की विफलता कैश-लाइन ओनरशिप से आती है। आठ थ्रेड्स आठ एड्रेस पर लिखते हैं, लेकिन वे एड्रेस एक ही कोहेरेंस लाइन में आते हैं। प्रत्येक राइट अन्य कोरों द्वारा रखी गई प्रतियों को अमान्य कर सकता है, और अगले राइटर को ओनरशिप फिर से हासिल करनी होगी, इसलिए लाइन लगातार ट्रांसफर होती रहती है। memory_order_relaxed क्रॉस-ऑब्जेक्ट ऑर्डरिंग को हटाता है, लेकिन यह अभी भी एक राइट है और कोहेरेंस को बायपास नहीं कर सकता है।
मैं थ्रेड्स को अलग-अलग फिजिकल कोर पर पिन करूँगा, वर्कलोड को स्थिर रखूँगा, एक से आठ थ्रेड्स तक थ्रूपुट मापूँगा, और HITM हॉटस्पॉट्स को ऑब्जेक्ट एड्रेस और फ़ील्ड ऑफ़सेट पर मैप करने के लिए perf c2c का उपयोग करूँगा। यदि विभिन्न थ्रेड्स एक लाइन में अलग-अलग काउंटरों को हिट करते हैं, और स्लॉट्स को कार्यान्वयन के डिस्ट्रक्टिव-इंटरफेरेंस साइज़ द्वारा अलग करने से HITM और बीता हुआ समय दोनों कम हो जाते हैं, तो यह फॉल्स शेयरिंग का प्रमाण है।
मैं पहले शेयरिंग को कम करूँगा: एक थ्रेड-लोकल काउंटर का उपयोग करूँगा और जब लाइव रीड्स की आवश्यकता न हो तो एक बार पब्लिश करूँगा। लाइव रीड्स के लिए, कैश-लाइन-सेपरेटेड एटॉमिक शार्ड्स का उपयोग करूँगा और पढ़ने पर उनका योग करूँगा। मैं सत्यापित करूँगा कि कुल योग 400 मिलियन बना रहे, प्रति थ्रेड कार्य समान हो, स्पीडअप दोहराया जा सके, और स्लॉट-स्पेस की आठ गुना लागत एक बड़ी कैश या TLB समस्या पैदा न करे।"
चरण-दर-चरण विस्तृत विश्लेषण
चरण 1: कैश-लाइन के संदर्भ में अड़चन (bottleneck) की व्याख्या करें
कैश कोहेरेंस लाइनों को ट्रैक करता है। कई कोर एक साथ केवल-पढ़ने योग्य प्रतियां रख सकते हैं। इससे पहले कि कोई कोर लाइन में एक बाइट भी लिखे, उसे ऐसी स्थिति प्राप्त करनी होगी जो संशोधन की अनुमति दे और अन्य कोरों में प्रतियों को अमान्य करे। उसी लाइन में दूसरा बाइट लिखने वाला अगला कोर इस ट्रांसफर को दोहराता है।
इस समस्या में प्रत्येक थ्रेड केवल अपने काउंटर पर लिखता है, इसलिए प्रोग्राम में शेयर्ड वेरिएबल पर कोई सिमेंटिक कंटेंशन नहीं है। हार्डवेयर एक कोहेरेंस यूनिट पर बार-बार राइट्स देखता है। शेयरिंग "फॉल्स" है क्योंकि यह किसी एल्गोरिद्मिक निर्भरता के बजाय फिजिकल लेआउट से आती है। एटॉमिक्स प्रत्येक मान की रक्षा करते हैं; वे यह वादा नहीं करते हैं कि कई आसन्न एटॉमिक ऑब्जेक्ट्स स्वतंत्र रूप से स्केल करेंगे।
केवल-पढ़ने योग्य शेयरिंग सामान्य रूप से शेयर्ड प्रतियों की अनुमति देती है। बार-बार होने वाले राइट्स ओनरशिप ट्रांसफर को संचालित करते हैं, इसलिए सामान्य रूप से पढ़े जाने वाले सभी डेटा को समस्या बताने के बजाय एक लाइन पर लिखने वाले विभिन्न कोरों की तलाश करें।
चरण 2: पैडिंग के साथ अनुमान लगाने के बजाय निदान को सिद्ध करें
साक्ष्य के चार समूह बनाएं:
- समान कार्य के साथ 1, 2, 4 और 8 थ्रेड्स को मापें, प्रति सेकंड इंक्रीमेंट और प्रति ऑपरेशन समय रिपोर्ट करें।
- थ्रेड्स को अलग-अलग फिजिकल कोर पर पिन करें और CPU माइग्रेशन, कॉन्टेक्स्ट स्विच और NUMA प्लेसमेंट रिकॉर्ड करें।
- प्रत्येक स्लॉट एड्रेस और ऑफ़सेट प्रिंट करें, विभिन्न राइटर्स, विभिन्न एड्रेस और एक ही लाइन की पुष्टि करें।
- कैश-टू-कैश ट्रांसफर एकत्र करें और उन्हें सोर्स और डेटा ऑब्जेक्ट्स पर मैप करें।
Linux पर, एक पुनरुत्पादक बेंचमार्क उपयोग कर सकता है:
perf c2c record -g -- ./counter-bench packed
perf c2c report --call-graph noneHITM का अर्थ है कि एक लोड किसी अन्य कैश में संशोधित लाइन से टकराया। यह इस दावे का समर्थन करता है कि संशोधित-लाइन ट्रांसफर हुआ, लेकिन यह अपने आप में फॉल्स शेयरिंग साबित नहीं करता है। एड्रेस, ऑफ़सेट और राइटर का निरीक्षण करें। यदि सभी थ्रेड्स एक काउंटर को अपडेट करते हैं, तो वह ट्रू शेयरिंग है। संरक्षित डेटा के बगल में एक लॉक भी इसी तरह का पैटर्न बना सकता है।
चरण 3: लेआउट के माध्यम से हॉट राइटर स्लॉट को अलग करें
C++17 एक कार्यान्वयन-परिभाषित डिस्ट्रक्टिव-इंटरफेरेंस साइज़ को प्रदर्शित करता है। निम्नलिखित एरे एलिमेंट्स में वह अलाइनमेंट है, और प्रत्येक एलिमेंट का साइज़ कम से कम समान अंतराल का है, जो पड़ोसी काउंटरों को एक ही डिस्ट्रक्टिव-इंटरफेरेंस क्षेत्र में पैक होने से रोकता है:
#include <array>
#include <atomic>
#include <cstdint>
#include <new>
struct PackedCounter {
std::atomic<std::uint64_t> value{0};
};
struct alignas(std::hardware_destructive_interference_size) SeparatedCounter {
std::atomic<std::uint64_t> value{0};
};
static_assert(
sizeof(SeparatedCounter) >= std::hardware_destructive_interference_size
);
std::array<PackedCounter, 8> packed;
std::array<SeparatedCounter, 8> separated;कार्यान्वयन यह स्थिरांक प्रदान करता है, इसलिए बिल्ड टूलचेन और निष्पादन टार्गेट का मिलान होना आवश्यक है। यदि टार्गेट लाइब्रेरी में इसकी कमी है, तो समर्थित प्लेटफ़ॉर्म के मान्य गुणों से लेआउट नीति प्राप्त करें और एड्रेस तथा प्रदर्शन को सत्यापित करें। 64 को एक सार्वभौमिक मान के रूप में हार्डकोड करना एक मशीन पर सही अवलोकन को पोर्टेबिलिटी के साथ भ्रमित करना है।
एक एरे के लिए, तीन चीजों का निरीक्षण करें: पहले तत्व का अलाइनमेंट, तत्व का स्ट्राइड, और प्रत्येक तत्व के अंदर हॉट फ़ील्ड का ऑफ़सेट। 8-बाइट स्ट्राइड को बनाए रखते हुए केवल एरे के पहले एड्रेस को अलाइन करने से काउंटर अलग नहीं होते हैं। फ़ील्ड बदलने पर एड-हॉक ट्रेलिंग पैडिंग भी टूट सकती है।
चरण 4: शेयर्ड राइट्स को हटाने को प्राथमिकता दें
लाइन सेपरेशन अभी भी 400 मिलियन एटॉमिक रीड-मॉडिफ़ाई-राइट्स निष्पादित करता है। यदि कुल योग की आवश्यकता केवल कार्य समाप्त होने के बाद होती है, तो प्रत्येक थ्रेड एक रजिस्टर या स्टैक-लोकल साधारण इंटीजर में गणना कर सकता है, बाहर निकलने से पहले एक आंशिक परिणाम प्रकाशित कर सकता है, और मुख्य थ्रेड को join के बाद रिडक्शन करने दे सकता है। शेयर्ड पब्लिकेशन प्रति थ्रेड 50 मिलियन ऑपरेशन्स से घटकर केवल एक रह जाता है।
यदि मॉनिटरिंग को लगभग-लाइव मान स्क्रैप करना चाहिए, तो गैर-हस्तक्षेप वाले स्लॉट में प्रति-थ्रेड या प्रति-कोर शार्ड बनाए रखें। रीडर आठ शार्ड्स का योग करता है। यह रीड एम्प्लीफिकेशन और एक क्षणिक असंगत स्नैपशॉट जोड़ता है। एक सख्ती से लीनियरिज़ेबल कुल योग एक एटॉमिक के साथ सरल है, लेकिन वह फिर से ट्रू शेयरिंग पेश करता है; उत्तर में स्पष्ट होना चाहिए कि संगति जीतती है या राइट थ्रूपुट।
बैचिंग एक मध्यम मार्ग है। एक वर्कर स्थानीय रूप से संचय करता है और समय-समय पर एक ग्लोबल काउंटर पर fetch_add लागू करता है। यह ओनरशिप ट्रांसफर को कम करता है लेकिन दृश्यमान कुल योग को प्रति राइटर अधिकतम एक बैच तक पिछड़ने देता है। अनुमत बासीपन (staleness) और मापों के आधार पर बैच का चयन करें।
चरण 5: स्पेस, लोकैलिटी और रखरखाव लागत की तुलना करें
इस समस्या में मापे गए 8-बाइट ऑब्जेक्ट और 64-बाइट इंटरफेरेंस अंतराल के साथ, एक अलग किया गया स्लॉट कॉम्पैक्ट स्लॉट का आठ गुना है। आठ वर्कर स्लॉट सस्ते हैं। दस लाख संस्थाओं में से प्रत्येक के लिए एक काउंटर को पैड करने से वर्किंग सेट का विस्तार होगा और कैश तथा पेज-टेबल का दबाव बढ़ेगा।
केवल उन फ़ील्ड्स को अलग करें जो विभिन्न कोरों पर बार-बार लिखने वाले साबित हुए हैं। एक साथ पढ़े जाने वाले और शायद ही कभी लिखे जाने वाले फ़ील्ड्स कॉम्पैक्ट रह सकते हैं। कम आवृत्ति वाले आंकड़े बैचों में प्रकाशित हो सकते हैं। बड़े एंटिटी सेट एंटिटी द्वारा पैड करने के बजाय थ्रेड द्वारा शार्ड किए जा सकते हैं। ऑप्टिमाइज़ेशन का लक्ष्य मापा गया ओनरशिप ट्रांसफर है, न कि किसी स्ट्रक्चर का दिखना।
लेआउट रिग्रेशन से बचाव करें। फ़ील्ड जोड़ना, इनहेरिटेंस बदलना, एलोकेटर बदलना या संकलन टार्गेट बदलना स्ट्राइड को बदल सकता है। लेआउट एसरशन्स, एड्रेस चेक और एक केंद्रित परफॉर्मेंस बेंचमार्क इस दावे वाली टिप्पणी की तुलना में अधिक टिकाऊ हैं कि संरचना 64 बाइट्स की है।
चरण 6: अन्य बाधाओं को दूर करने के लिए काउंटरफैक्चुअल प्रयोगों का उपयोग करें
कम से कम तीन संस्करणों का परीक्षण करें: एक पैक्ड एटॉमिक एरे, एक सेपरेटेड एटॉमिक एरे, और थ्रेड-लोकल काउंटिंग के बाद रिडक्शन। यदि केवल बाद के दो संस्करण स्केल करते हैं और कैश-लाइन ट्रांसफर उनके साथ गिरते हैं, तो कारण का मामला बहुत मजबूत हो जाता है।
यदि अलग करने के बाद भी गति धीमी रहती है, तो एक शेयर्ड कंट्रोल वेरिएबल, एटॉमिक निर्देशों की थ्रूपुट सीमा, रिमोट NUMA मेमोरी, CPU माइग्रेशन, थ्रेड-स्टार्ट और सिंक्रोनाइज़ेशन लागतों के लिए बहुत छोटा वर्कलोड, और संतृप्त मेमोरी बैंडविड्थ का निरीक्षण करें। फॉल्स शेयरिंग उन बाधाओं के साथ सह-अस्तित्व में हो सकती है।
एकल सबसे तेज़ रन की रिपोर्ट न करें। वार्म अप करें, दोहराएं, और कंपाइलर फ़्लैग, फ़्रीक्वेंसी नीति, थ्रेड टोपोलॉजी और इनपुट को स्थिर रखते हुए माध्यिका (median) और प्रसार (spread) की रिपोर्ट करें। प्रत्येक प्रदर्शन परिणाम के लिए शुद्धता जांच की भी आवश्यकता होती है: काउंटर या कम किया गया मान अभी भी ठीक 400 मिलियन के बराबर होना चाहिए।
उच्च गुणवत्ता वाला नमूना उत्तर
"मैं पहले शुद्धता को स्केलेबिलिटी से अलग करूँगा। आठ थ्रेड्स आठ अलग-अलग एटॉमिक ऑब्जेक्ट्स को अपडेट करते हैं, इसलिए रिलैक्स्ड एटॉमिक्स प्रत्येक काउंटर को सुरक्षित रख सकते हैं। हालाँकि, ऑब्जेक्ट्स एक ही कैश लाइन में रहते हैं, और हार्डवेयर लाइन द्वारा राइट ओनरशिप प्रदान करता है। कोर 0 द्वारा अपने 8 बाइट्स को संशोधित करने के बाद, कोर 1 को अभी भी एक अलग 8 बाइट्स को संशोधित करने के लिए पूरी लाइन के स्वामित्व की आवश्यकता होती है और यह कोर 0 की कॉपी को अमान्य कर देता है। जैसे-जैसे राइटर्स बदलते हैं, लाइन कोरों के बीच ट्रांसफर होती रहती है और फिजिकल लेआउट स्वतंत्र काउंटरों को सीरियलाइज़ करता है। रिलैक्स्ड ऑपरेशन्स के बीच ऑर्डरिंग गारंटी को हटाता है, कैश कोहेरेंस को नहीं।
मैं केवल स्केलिंग वक्र से निष्कर्ष नहीं निकालूँगा। मैं 1, 2, 4 और 8 थ्रेड्स को अलग-अलग फिजिकल कोर पर पिन करूँगा, प्रति थ्रेड 50 मिलियन ऑपरेशन्स को बनाए रखूँगा, और थ्रूपुट, माइग्रेशन और टोपोलॉजी रिकॉर्ड करूँगा। फिर मैं HITM को एरे-एलिमेंट ऑफ़सेट पर मैप करने के लिए perf c2c का उपयोग करूँगा। एक लाइन में विभिन्न ऑफ़सेट पर विभिन्न राइटर्स फॉल्स शेयरिंग का संकेत देते हैं। समान ऑफ़सेट ट्रू शेयरिंग का सुझाव देता है, जबकि लॉक्स और NUMA को अलग से जांचने की आवश्यकता होती है।
लाइव रीड्स के लिए, मैं प्रत्येक शार्ड को std::hardware_destructive_interference_size पर अलाइन करूँगा, यह सुनिश्चित करूँगा कि एरे स्ट्राइड कम से कम वह मान हो, और पढ़ने पर शार्ड्स का योग करूँगा। यदि रीड्स केवल काम खत्म होने पर होते हैं, तो एक पब्लिकेशन और join के बाद रिडक्शन वाले थ्रेड-लोकल साधारण इंटीजर बेहतर होते हैं क्योंकि वे हॉट पाथ से शेयरिंग को हटा देते हैं।
मैं एक ही मशीन और बिल्ड पर पैक्ड, सेपरेटेड और लोकल-रिडक्शन संस्करणों को बार-बार बेंचमार्क करूँगा। सभी कुल योग 400 मिलियन बने रहने चाहिए; काउंटर लाइन का HITM और बीता हुआ समय अलग होने के बाद एक साथ गिरना चाहिए, जबकि स्थानीय रिडक्शन को अधिक एटॉमिक लागत को हटाना चाहिए। मैं स्पेस लागत भी रिकॉर्ड करूँगा: इस टार्गेट पर, एक स्लॉट 8 बाइट्स से बढ़कर कम से कम 64 हो जाता है, जो आठ गुना वृद्धि है जिसे दस लाख कोल्ड काउंटरों पर अंधाधुंध लागू नहीं किया जाना चाहिए।"
सामान्य गलतियाँ
- यह दावा करना कि एटॉमिक्स फॉल्स-शेयर नहीं कर सकते → एटॉमिक्स ऑब्जेक्ट ऑपरेशन्स को अविभाज्य बनाते हैं लेकिन कोहेरेंस ग्रैन्युलैरिटी को नहीं बदलते हैं → शुद्धता को लाइन ओनरशिप से अलग करें।
- यह दावा करना कि रिलैक्स्ड कोहेरेंस को अक्षम करता है → यह भाषा-स्तरीय ऑर्डरिंग को कमजोर करता है जबकि राइट्स कोरों के बीच कोहेरेंट बने रहते हैं → एटॉमिकिटी, ऑर्डरिंग और हार्डवेयर कोहेरेंस में अंतर करें।
- केवल HITM से फॉल्स शेयरिंग घोषित करना → ट्रू शेयरिंग और लॉक फ़ील्ड्स भी संशोधित लाइनों को ट्रांसफर करते हैं → एड्रेस, ऑफ़सेट और राइटर्स को मैप करें।
- केवल एरे बेस को अलाइन करना → 8-बाइट एलिमेंट्स अभी भी एक ही लाइन में आ सकते हैं → एलिमेंट अलाइनमेंट और स्ट्राइड दोनों को नियंत्रित करें।
- हमेशा 64 बाइट्स को हार्डकोड करना → इंटरफेरेंस साइज़ कार्यान्वयन और टार्गेट पर निर्भर करता है → कार्यान्वयन मान या मान्य प्लेटफ़ॉर्म नीति का उपयोग करें और लेआउट की पुन: जांच करें।
- प्रत्येक फ़ील्ड को पैड करना → वर्किंग-सेट, कैश और TLB लागत लाभ से अधिक हो सकती है → केवल सिद्ध हॉट क्रॉस-कोर राइटर्स को अलग करें।
- एक टाइमिंग रन की तुलना करना → फ़्रीक्वेंसी, माइग्रेशन और वार्म-अप शोर पैदा करते हैं → टोपोलॉजी को पिन करें, दोहराएं, और HITM तथा शुद्धता की जांच करें।
- लोकल रिडक्शन को अनदेखा करना → पैडिंग लेआउट में सुधार करती है लेकिन एटॉमिक कार्य को बनाए रखती है → ताज़गी की जरूरतों के अनुसार शेयर्ड राइट्स को कम करें।
फॉलो-अप प्रश्न और उत्तर
फॉलो-अप 1: क्या memory_order_relaxed एटॉमिक्स को साधारण इंटीजर्स से बदलने पर यह ठीक हो जाता है?
यदि प्रत्येक थ्रेड स्थायी रूप से एक अलग एलिमेंट का मालिक है, तो साधारण इंटीजर्स डेटा रेस नहीं बनाते हैं, लेकिन आसन्न एलिमेंट्स अभी भी फॉल्स-शेयर कर सकते हैं। थ्रेड-लोकल साधारण इंटीजर्स तब सबसे अच्छे होते हैं जब पढ़ना join के बाद होता है। यदि कोई अन्य थ्रेड शेयर्ड एरे को समवर्ती रूप से पढ़ता है, तो आपको केवल एटॉमिक्स को हटाने के बजाय सिंक्रोनाइज़ेशन और दृश्यता को फिर से स्थापित करना होगा।
फॉलो-अप 2: अलग करने के बाद भी HITM शून्य क्यों नहीं हो सकता है?
प्रोग्राम में अभी भी स्टार्ट बैरियर, वर्क कतार, लॉक, एलोकेटर मेटाडेटा या ग्लोबल प्रोग्रेस वेरिएबल में ट्रू शेयरिंग हो सकती है, और रीडर शार्ड्स को छूता है। पहले पुष्टि करें कि मूल काउंटर लाइन का हॉटस्पॉट गिर गया है, फिर शेष एड्रेस का निरीक्षण करें। लक्ष्य व्यावसायिक निर्भरता के बिना ट्रांसफर को हटाना है, न कि हर जगह शून्य HITM का वादा करना।
फॉलो-अप 3: थ्रेड-लोकल रिडक्शन लाइव मेट्रिक्स का समर्थन कैसे कर सकता है?
प्रत्येक वर्कर को प्रति बैच एक बार अपने स्थानीय डेल्टा को एक अलग शार्ड में प्रकाशित करने दें, और स्क्रैपर को शार्ड्स का योग करने दें। बड़े बैच राइट ट्रैफ़िक को कम करते हैं लेकिन अवलोकनों को अधिक बासी बना देते हैं; छोटे बैच ताज़गी में सुधार करते हैं लेकिन कंटेंशन बढ़ाते हैं। अधिकतम स्वीकार्य बासीपन बताएं, उस सीमा से एक बैच चुनें, और इसे मापें।
फॉलो-अप 4: एक ग्लोबल एटॉमिक काउंटर का उपयोग क्यों न करें?
यह सबसे कम जगह का उपयोग करता है, पढ़ने में आसान है, और एक संशोधन क्रम प्रदान करता है, लेकिन प्रत्येक राइटर एक ही ऑब्जेक्ट को संशोधित करता है, जिससे ट्रू शेयरिंग बनती है। यह कम अपडेट दर या मजबूत स्थिरता आवश्यकता के लिए सही हो सकता है। उच्च-आवृत्ति के आंकड़े आमतौर पर शार्डिंग और रीड-टाइम एकत्रीकरण का समर्थन करते हैं। फॉल्स शेयरिंग को ठीक करने से कॉन्ट्रैक्ट द्वारा आवश्यक ट्रू शेयरिंग को हटाया नहीं जा सकता है।
फॉलो-अप 5: क्या होगा यदि डिप्लॉयमेंट मशीनों में बिल्ड मशीन से अलग कैश-लाइन साइज़ हो?
मानक-लाइब्रेरी मान एक कार्यान्वयन-परिभाषित बिल्ड-टाइम संपत्ति है। विषम (heterogeneous) हार्डवेयर के लिए इच्छित एकल बाइनरी को समर्थित टार्गेट्स में एक सत्यापित ABI और इंटरफेरेंस अंतराल की आवश्यकता होती है। उन टार्गेट्स को कवर करने वाला एक रूढ़िवादी (conservative) लेआउट चुनें या प्रति टार्गेट बिल्ड करें, फिर प्रत्येक मशीन क्लास पर एड्रेस और प्रदर्शन सत्यापन चलाएं। एक डेवलपमेंट होस्ट पर 64-बाइट का अवलोकन प्रत्येक डिप्लॉयमेंट लेआउट को स्थापित नहीं कर सकता है।