मैं सोच रहा हूं कि 2007 से उलरिच ड्रेपर की व्हाट एवरी प्रोग्रामर को मेमोरी के बारे में कितना पता होना चाहिए । इसके अलावा, मैं 1.0 या एक शुद्धिपत्र से एक नए संस्करण नहीं पा सके।
मैं सोच रहा हूं कि 2007 से उलरिच ड्रेपर की व्हाट एवरी प्रोग्रामर को मेमोरी के बारे में कितना पता होना चाहिए । इसके अलावा, मैं 1.0 या एक शुद्धिपत्र से एक नए संस्करण नहीं पा सके।
जवाबों:
जहां तक मुझे याद है कि ड्रेपर की सामग्री मेमोरी के बारे में मौलिक अवधारणाओं का वर्णन करती है: सीपीयू कैश कैसे काम करता है, भौतिक और आभासी मेमोरी क्या हैं और लिनक्स कर्नेल उस चिड़ियाघर को कैसे डील करते हैं। संभवतः कुछ उदाहरणों में पुराने एपीआई संदर्भ हैं, लेकिन इससे कोई फर्क नहीं पड़ता; कि मूलभूत अवधारणाओं की प्रासंगिकता को प्रभावित नहीं करेगा।
इसलिए, कोई भी पुस्तक या लेख जो कुछ मौलिक का वर्णन करता है उसे पुराना नहीं कहा जा सकता। "प्रत्येक प्रोग्रामर को स्मृति के बारे में क्या जानना चाहिए" निश्चित रूप से पढ़ने लायक है, लेकिन, ठीक है, मुझे नहीं लगता कि यह "हर प्रोग्रामर" के लिए है। यह सिस्टम / एम्बेडेड / कर्नेल लोगों के लिए अधिक उपयुक्त है।
PDF के रूप में गाइड पर है https://www.akkadia.org/drepper/cpumemory.pdf ।
यह अभी भी आम तौर पर उत्कृष्ट और अत्यधिक अनुशंसित है (मेरे द्वारा, और मुझे लगता है कि अन्य प्रदर्शन-ट्यूनिंग विशेषज्ञों द्वारा)। यह अच्छा होगा यदि उलरिच (या किसी और) ने 2017 अपडेट लिखा, लेकिन यह बहुत काम होगा (जैसे बेंचमार्क को फिर से चलाना)। अन्य x86 प्रदर्शन-ट्यूनिंग और SSE / asm (और C / C ++) अनुकूलन लिंक भी देखें86 टैग विकि । (उलरिच का लेख x86 विशिष्ट नहीं है, लेकिन उसके बेंचमार्क के अधिकांश (सभी) x86 हार्डवेयर पर हैं।)
डीआरएएम और कैश कैसे काम करते हैं, इसके बारे में निम्न स्तर के हार्डवेयर विवरण अभी भी लागू होते हैं । DDR4 DDR1 / DDR2 (पढ़ें / लिखें फट) के लिए वर्णित समान कमांड का उपयोग करता है । DDR3 / 4 सुधार बुनियादी बदलाव नहीं हैं। AFAIK, सभी आर्क-स्वतंत्र सामान अभी भी आम तौर पर लागू होते हैं, उदाहरण के लिए AArch64 / ARM32।
यह भी देखें इस जवाब की अदृश्यता बाउंड प्लेटफार्म अनुभाग एकल थ्रेड बैंडविड्थ पर स्मृति / एल 3 विलंबता के प्रभाव के बारे में महत्वपूर्ण जानकारी के लिए: , और यह वास्तव में एक जिऑन की तरह एक आधुनिक कई-कोर CPU पर एकल थ्रेड बैंडविड्थ के लिए प्राथमिक अड़चन है । लेकिन एक क्वाड-कोर Skylake डेस्कटॉप एक धागे के साथ DRAM बैंडविड्थ को अधिकतम करने के करीब आ सकता है। उस लिंक में NT स्टोर बनाम x86 पर सामान्य स्टोर के बारे में कुछ बहुत अच्छी जानकारी है। एकल-थ्रेडेड मेमोरी थ्रूपुट के लिए ब्रॉडवेल-ई की तुलना में स्काईलेक इतना बेहतर क्यों है? एक सारांश है।bandwidth <= max_concurrency / latency
इस प्रकार 6.5.8 में उलरिच का सुझाव अन्य NUMA नोड्स पर रिमोट मेमोरी का उपयोग करने के बारे में सभी बैंडविड्थ का उपयोग करने के साथ-साथ आधुनिक हार्डवेयर पर काउंटर-उत्पादक है, जहां मेमोरी नियंत्रकों के पास एकल कोर की तुलना में अधिक बैंडविड्थ है। अच्छी तरह से संभवतः आप एक ऐसी स्थिति की कल्पना कर सकते हैं जहां कम-विलंबता अंतर-थ्रेड संचार के लिए एक ही NUMA नोड पर कई मेमोरी-भूखे धागे चलाने के लिए एक शुद्ध लाभ है, लेकिन उच्च बैंडविड्थ नहीं-विलंबता-संवेदनशील सामान के लिए दूरस्थ स्मृति का उपयोग कर रहे हैं। लेकिन यह बहुत अस्पष्ट है, आम तौर पर सिर्फ NUMA नोड्स के बीच धागे विभाजित करते हैं और उन्हें स्थानीय मेमोरी का उपयोग करते हैं। अधिकतम-संगति सीमा (नीचे देखें) के कारण प्रति-कोर बैंडविड्थ विलंबता के प्रति संवेदनशील है, लेकिन एक सॉकेट में सभी कोर आमतौर पर उस सॉकेट में मेमोरी कंट्रोलर को संतृप्त करने से अधिक हो सकते हैं।
एक बड़ी बात जो बदली है, वह यह है कि हार्डवेयर प्रीफैच पेन्टियम 4 की तुलना में बहुत बेहतर है और एक से बड़े स्ट्राइड तक तार के एक्सेस पैटर्न को पहचान सकता है, और एक ही बार में कई स्ट्रीम (जैसे एक फॉरवर्ड / बैकवर्ड प्रति 4k पेज)। इंटेल के अनुकूलन मैनुअल ने अपने सैंडीब्रिज-परिवार माइक्रोआर्किटेक्चर के लिए कैश के विभिन्न स्तरों में एचडब्ल्यू प्रीफैचर्स के कुछ विवरणों का वर्णन किया है। आईवीब्रिज और बाद में अगले पेज के हार्डवेयर प्रीफ़ैच होते हैं, इसके बजाय नए पेज में कैश-मिस की प्रतीक्षा करने के लिए तेजी से शुरुआत करने के लिए। मुझे लगता है कि एएमडी में उनके अनुकूलन मैनुअल में कुछ समान सामान हैं। खबरदार कि इंटेल का मैनुअल भी पुरानी सलाह से भरा है, जिनमें से कुछ पी 4 के लिए ही अच्छे हैं। सैंडीब्रिज-विशिष्ट खंड एसएनबी के लिए निश्चित रूप से सटीक हैं, लेकिन उदाहरण के लिएयूएसडब्ल्यू में माइक्रो-फ़्यूज़ किए गए यूओपी का लेमिनेशन बदल गया है और मैनुअल इसका उल्लेख नहीं करता है ।
इन दिनों सामान्य सलाह यह है कि सभी SW प्रीफ़ैच को पुराने कोड से हटा दिया जाए , और केवल इसे वापस रखने पर विचार करें यदि प्रोफाइलिंग कैशे मिस करती है (और आप मेमोरी बैंडविड्थ को संतृप्त नहीं कर रहे हैं)। बाइनरी खोज के अगले चरण के दोनों किनारों को प्रीफ़ैच करना अभी भी मदद कर सकता है। उदाहरण के लिए, एक बार जब आप यह तय कर लेते हैं कि कौन सा तत्व आगे देखना है, तो 1/4 और 3/4 तत्वों को प्रीफ़ैच करें ताकि वे लोडिंग या चेकिंग मध्य के साथ समानांतर में लोड हो सकें।
एक अलग प्रीफ़ैच थ्रेड (6.3.4) का उपयोग करने का सुझाव पूरी तरह से अप्रचलित है , मुझे लगता है, और केवल पेंटियम 4 पर कभी अच्छा था। पी 4 में हाइपरथ्रेडिंग (एक भौतिक कोर को साझा करने वाले 2 तार्किक कोर) थे, लेकिन पर्याप्त ट्रेस-कैश नहीं था (और / या आउट-ऑफ-ऑर्डर निष्पादन संसाधन) एक ही कोर पर दो पूर्ण गणना सूत्र चलाने वाले थ्रूपुट प्राप्त करने के लिए। लेकिन आधुनिक सीपीयू (सैंडीब्रिज-परिवार और रायज़ेन) बहुत अधिक बीफ़ियर हैं और उन्हें या तो एक असली धागा चलाना चाहिए या हाइपरथ्रेडिंग का उपयोग नहीं करना चाहिए (अन्य तार्किक कोर निष्क्रिय छोड़ दें ताकि सोल थ्रेड में आरओबी विभाजन के बजाय पूर्ण संसाधन हों)।
सॉफ्टवेयर प्रीफैच हमेशा "भंगुर" रहा है : स्पीडअप पाने के लिए सही मैजिक ट्यूनिंग नंबर हार्डवेयर के विवरण और शायद सिस्टम लोड पर निर्भर करता है। बहुत जल्दी और यह डिमांड लोड से पहले बेदखल है। बहुत देर हो चुकी है और यह मदद नहीं करता है। यह ब्लॉग लेख एक समस्या के गैर-अनुक्रमिक भाग को प्रीफ़ेट करने के लिए हैशवेल पर SW प्रीफ़च का उपयोग करने में एक दिलचस्प प्रयोग के लिए कोड + ग्राफ़ दिखाता है। यह भी देखें कि प्रीफ़ैच निर्देशों का ठीक से उपयोग कैसे करें? । NT प्रीफैच दिलचस्प है, लेकिन इससे भी अधिक भंगुर है क्योंकि L1 से एक प्रारंभिक निष्कासन का मतलब है कि आपको L3 ही नहीं, बल्कि L3 या DRAM के लिए सभी तरह से जाना होगा। यदि आपको प्रदर्शन के प्रत्येक अंतिम ड्रॉप की आवश्यकता है, और आप एक विशिष्ट मशीन के लिए ट्यून कर सकते हैं, तो SW प्रीफैच अनुक्रमिक रूप से देखने लायक है, लेकिन यहयदि स्मृति में अड़चनों के करीब आने पर आपके पास पर्याप्त ALU कार्य करने के लिए अभी भी एक मंदी हो सकती है ।
कैश लाइन का आकार अभी भी 64 बाइट्स है। (L1D पढ़ने / लिखने की बैंडविड्थ बहुत अधिक है, और आधुनिक CPU प्रति घड़ी 2 वेक्टर लोड + 1 वेक्टर स्टोर कर सकते हैं यदि यह सभी L1D में हिट करता है। देखें कि कैश कितनी तेजी से हो सकता है? ) AVX512 के साथ, लाइन आकार = वेक्टर चौड़ाई। इसलिए आप एक निर्देश में एक संपूर्ण कैश लाइन को लोड / स्टोर कर सकते हैं। इस प्रकार हर गलत लोड लोड / स्टोर 256b AVX1 / AVX2 के लिए हर दूसरे के बजाय कैश-लाइन की सीमा को पार करता है, जो अक्सर उस सरणी पर लूपिंग को धीमा नहीं करता है जो L1D में नहीं था।
यदि रनटाइम पर पता संरेखित किया जाता है, तो अनलोड किए गए लोड निर्देशों में शून्य जुर्माना होता है, लेकिन कंपाइलर (विशेष रूप से जीसीसी) किसी भी संरेखण गारंटी के बारे में पता होने पर ऑटोवेराइजिंग करते समय बेहतर कोड बनाते हैं। वास्तव में अनलग्ड ऑप्स आम तौर पर तेज़ होते हैं, लेकिन पेज-स्प्लिट्स अभी भी चोटिल होते हैं (स्काइलेक पर बहुत कम, हालांकि, केवल ~ 11 अतिरिक्त चक्र विलंबता बनाम 100, लेकिन फिर भी एक थ्रूपुट पेनल्टी)।
जैसा कि उलरिच ने भविष्यवाणी की थी, इन दिनों हर मल्टी-सॉकेट सिस्टम एनयूएमए है: एकीकृत मेमोरी कंट्रोलर मानक हैं, अर्थात कोई अतिरिक्त पोर्टब्रिज नहीं है। लेकिन एसएमपी का मतलब अब मल्टी-सॉकेट नहीं है, क्योंकि मल्टी-कोर सीपीयू व्यापक हैं। Nehalem से Skylake तक के Intel CPUs ने कोर के बीच सुसंगतता के लिए एक बड़े समावेशी L3 कैश का उपयोग किया है । एएमडी सीपीयू अलग हैं, लेकिन मैं विवरणों पर स्पष्ट नहीं हूं।
Skylake-X (AVX512) में अब समावेशी L3 नहीं है, लेकिन मुझे लगता है कि अभी भी एक टैग निर्देशिका है जो यह जांचने देती है कि चिप पर कहीं भी कैश्ड (और यदि ऐसा है) तो वास्तव में सभी कोर के लिए स्नूप को प्रसारित किए बिना। SKX रिंग बस के बजाय एक जाली का उपयोग करता है , आम तौर पर पिछले कई-कोर Xeons की तुलना में इससे भी बदतर विलंबता, दुर्भाग्य से।
मूल रूप से मेमोरी प्लेसमेंट को अनुकूलित करने के बारे में सभी सलाह अभी भी लागू होती है, बस तब क्या होता है जब आप कैश मिस या विवाद से नहीं बच सकते हैं।
6.4.2 परमाणु ऑप्स : सीएएस-रिट्री लूप को हार्डवेयर-मध्यस्थता से 4x के रूप में दिखाने वाला बेंचमार्क lock add
शायद अभी भी अधिकतम विवाद मामले को दर्शाता है । लेकिन वास्तविक बहु-थ्रेडेड कार्यक्रमों में, सिंक्रनाइज़ेशन को न्यूनतम (क्योंकि यह महंगा है) रखा जाता है, इसलिए विवाद कम होता है और कैस-रिट्री लूप आमतौर पर पुन: प्रयास किए बिना सफल होता है।
C ++ 11 std::atomic
fetch_add
एक lock add
(या lock xadd
यदि वापसी मान का उपयोग किया जाता है) के लिए संकलित करेगा , लेकिन एक एल्गोरिथ्म जो किसी lock
एड अनुदेश के साथ नहीं किया जा सकता है कुछ करने के लिए CAS का उपयोग करना आमतौर पर एक आपदा नहीं है। जब तक आप परमाणु और गैर-परमाणु पहुंच को एक ही स्थान पर नहीं मिलाना चाहते हैं, तब तक Gcc विरासत में निर्मित या नए बिल्ट-इन के बजाय C ++ 11std::atomic
या C11 का उपयोग करें ...stdatomic
__sync
__atomic
8.1 DWCAS ( cmpxchg16b
) : आप इसे उत्सर्जित करने में जीसीसी कर सकते हैं, लेकिन यदि आप ऑब्जेक्ट के सिर्फ एक आधे हिस्से का कुशल भार चाहते हैं, तो आपको बदसूरत union
हैक्स की आवश्यकता है: मैं सी ++ 11 कैस के साथ एबीए काउंटर कैसे लागू कर सकता हूं? । ( 2 अलग मेमोरी स्थानों के DCAS के साथ DWCAS को भ्रमित न करें। DCAS के साथ DCAS का लॉक-फ्री परमाणु उत्सर्जन संभव नहीं है, लेकिन ट्रांसेक्शनल मेमोरी (जैसे x86 TSX) संभव बनाता है।)
8.2.4 ट्रांसेक्शनल मेमोरी : एक जोड़े की झूठी शुरुआत के बाद (शायद ही ट्रिगर बग के कारण माइक्रोकोड अपडेट द्वारा अक्षम किया गया), इंटेल ने देर-मॉडल ब्रॉडवेल और सभी स्काईलेक सीपीयू में ट्रांसेक्शनल मेमोरी का काम किया है। डिजाइन अभी भी है जो डेविड कंटर ने हसवेल के लिए वर्णित किया था । कोड को गति देने के लिए इसका उपयोग करने के लिए एक लॉक-एलेक्शन तरीका है (जो एक नियमित लॉक को उपयोग करता है (और वापस गिर सकता है) विशेष रूप से कंटेनर के सभी तत्वों के लिए एक ही लॉक के साथ एक ही महत्वपूर्ण अनुभाग में कई थ्रेड्स अक्सर टकराते नहीं हैं ), या कोड लिखने के लिए जो सीधे लेनदेन के बारे में जानता है।
7.5 विशालकाय: अनाम पारदर्शी विशालताएँ लिनक्स पर अच्छी तरह से काम करती हैं, बिना मैन्युअल रूप से hugetlbfs का उपयोग किए बिना। आवंटन करें = 2MiB संरेखण के साथ = 2MiB (उदाहरण के लिए posix_memalign
, याaligned_alloc
कि जब विफल होने के लिए बेवकूफ ISO C ++ 17 आवश्यकता को लागू नहीं करता है size % alignment != 0
)।
2MiB- संरेखित अनाम आवंटन डिफ़ॉल्ट रूप से विशालपृष्ठ का उपयोग करेगा। कुछ कार्यभार (जैसे कि उन्हें बनाने के बाद थोड़ी देर के लिए बड़े आवंटन का उपयोग करते रहें)
echo always >/sys/kernel/mm/transparent_hugepage/defrag
4k पृष्ठों पर वापस गिरने के बजाय, जब भी जरूरत हो, शारीरिक मेमोरी को डीफ़्रैग करने के लिए लाभ मिल सकता है । ( कर्नेल डॉक्स देखें )। वैकल्पिक रूप से, madvise(MADV_HUGEPAGE)
बड़े आवंटन करने के बाद उपयोग करें (अधिमानतः अभी भी 2MiB संरेखण के साथ)।
परिशिष्ट B: Oprofile : Linux perf
ने अधिकांशतः अधिगृहीत किया है oprofile
। कुछ विशिष्ट सूक्ष्म-विशिष्टताओं के लिए विशिष्ट घटनाओं के लिए, ocperf.py
आवरण का उपयोग करें । जैसे
ocperf.py stat -etask-clock,context-switches,cpu-migrations,page-faults,cycles,\
branches,branch-misses,instructions,uops_issued.any,\
uops_executed.thread,idq_uops_not_delivered.core -r2 ./a.out
इसका उपयोग करने के कुछ उदाहरणों के लिए, क्या कैन x86 का एमओवी वास्तव में "मुक्त" हो सकता है? मैं यह सब क्यों नहीं कर सकता? ।
मेरी त्वरित नज़र से-यह काफी सटीक दिखता है। ध्यान देने वाली एक बात, "एकीकृत" और "बाहरी" मेमोरी नियंत्रकों के बीच अंतर पर एक हिस्सा है। जब से i7 लाइन इंटेल सीपीयू की रिलीज हुई है, सभी एएमडी एकीकृत हैं, और एएमडी एकीकृत मेमोरी कंट्रोलरों का उपयोग कर रहा है क्योंकि एएमडी 64 चिप्स पहली बार जारी किए गए थे।
चूँकि यह लेख लिखा गया था, पूरे में बहुत बदलाव नहीं हुआ है, गति अधिक हो गई है, स्मृति नियंत्रकों ने बहुत अधिक बुद्धिमानी प्राप्त कर ली है (जब तक कि बदलावों को महसूस करने के लिए i7 रैम में देरी नहीं करता है) लिखता है, लेकिन एक बहुत कुछ नहीं बदला है । कम से कम किसी भी तरह से नहीं जो एक सॉफ्टवेयर डेवलपर के बारे में परवाह करेगा।