यहाँ पहले से ही बहुत सारे अच्छे उत्तर हैं जो कई मुख्य बिंदुओं को कवर करते हैं, इसलिए मैं सिर्फ कुछ मुद्दों को जोड़ूंगा जिन्हें मैंने ऊपर सीधे संबोधित नहीं किया था। यही है, इस जवाब को पेशेवरों और विपक्षों के व्यापक रूप से नहीं माना जाना चाहिए, बल्कि यहां अन्य उत्तरों के लिए एक परिशिष्ट है।
मैमप जादू की तरह लगता है
इस मामले में जहां फ़ाइल पहले से ही पूरी तरह से कैश किया गया है ले रहा है 1 आधार रेखा के रूप में 2 , mmap
की तरह काफी लग सकता है जादू :
mmap
केवल संपूर्ण फ़ाइल को मैप करने के लिए (संभावित) 1 सिस्टम कॉल की आवश्यकता होती है, जिसके बाद किसी और सिस्टम कॉल की आवश्यकता नहीं होती है।
mmap
कर्नेल से उपयोगकर्ता-स्थान पर फ़ाइल डेटा की प्रतिलिपि की आवश्यकता नहीं है।
mmap
आपको फ़ाइल को "मेमोरी के रूप में" एक्सेस करने की अनुमति देता है, जिसमें आप मेमोरी के खिलाफ जो भी उन्नत चालें कर सकते हैं, जैसे कि कंपाइलर ऑटो-वैरिफिकेशन, सिमड इंट्रिंसिक्स, प्रीफेटिंग, अनुकूलित-इन-मेमोरी पार्सिंग रूटीन, ओपनएमपी आदि शामिल हैं।
उस स्थिति में जब फ़ाइल पहले से ही कैश में है, इसे हरा पाना असंभव है: आप सीधे कर्नेल पेज कैश को मेमोरी के रूप में एक्सेस करते हैं और यह उससे तेज नहीं हो सकता है।
खैर, यह कर सकते हैं।
वास्तव में जादू नहीं है क्योंकि ...
mmap अभी भी प्रति पृष्ठ काम करता है
एक प्राथमिक छिपी हुई लागत mmap
बनाम read(2)
(जो वास्तव में पढ़ने वाले ब्लॉकों के लिए तुलनीय ओएस-स्तरीय syscall है ) यह है कि mmap
आपको उपयोगकर्ता-अंतरिक्ष में प्रत्येक 4K पृष्ठ के लिए "कुछ काम" करने की आवश्यकता होगी, भले ही यह छिपा हो। पृष्ठ-दोष तंत्र।
एक उदाहरण के लिए एक विशिष्ट कार्यान्वयन जो mmap
पूरी फ़ाइल को केवल 100 जीबी फ़ाइल को पढ़ने के लिए 100 जीबी / 4K = 25 मिलियन दोष में गलत करने की आवश्यकता होगी। अब, ये मामूली दोष होंगे , लेकिन 25 बिलियन पृष्ठ के दोष अभी भी सुपर फास्ट नहीं होंगे। एक मामूली गलती की कीमत शायद सबसे अच्छे मामले में नैनो के 100 के दशक में है।
एमएमएपी टीएलबी प्रदर्शन पर बहुत निर्भर करता है
अब, आप इसे यह बताने के MAP_POPULATE
लिए पास कर सकते हैं mmap
कि लौटने से पहले सभी पेज टेबल सेट कर लें, इसलिए इसे एक्सेस करते समय कोई पेज दोष नहीं होना चाहिए। अब, यह थोड़ी सी समस्या है कि यह पूरी फ़ाइल को RAM में भी पढ़ता है, जो कि यदि आप 100GB फ़ाइल को मैप करने का प्रयास करते हैं, तो आपको झटका देना होगा - लेकिन आइए अब 3 के लिए इसे अनदेखा करें । कर्नेल को इन पृष्ठ तालिकाओं (कर्नेल समय के रूप में दिखाता है) को सेट करने के लिए प्रति पृष्ठ कार्य करने की आवश्यकता है । यह mmap
दृष्टिकोण में एक प्रमुख लागत होने के नाते समाप्त होता है , और यह फ़ाइल आकार के लिए आनुपातिक है (यानी, यह फ़ाइल आकार बढ़ने के कारण अपेक्षाकृत कम महत्वपूर्ण नहीं है) 4 ।
अंत में, यहां तक कि उपयोगकर्ता-स्पेस में भी इस तरह की मैपिंग बिल्कुल मुफ्त नहीं है (बड़े मेमोरी बफ़र्स की तुलना में फ़ाइल-आधारित नहीं है mmap
) - यहां तक कि पेज टेबल सेट होने के बाद भी, नए पेज पर प्रत्येक एक्सेस हो रहा है, वैचारिक रूप से, टीएलबी की याद आती है। चूँकि mmap
किसी फ़ाइल का अर्थ है पृष्ठ कैश और उसके 4K पृष्ठों का उपयोग करना, तो आप फिर से 100GB फ़ाइल के लिए इस लागत को 25 मिलियन गुना कर सकते हैं।
अब, इन टीएलबी मिसेस की वास्तविक लागत आपके हार्डवेयर के कम से कम निम्नलिखित पहलुओं पर बहुत अधिक निर्भर करती है: (क) आपके पास कितने 4K टीएलबी हैं और शेष अनुवाद कैशिंग काम करता है (ख) कितना अच्छा हार्डवेयर प्रीफैच करता है TLB के साथ - उदाहरण के लिए, क्या पृष्ठ चलना ट्रिगर कर सकता है? (c) पेज वॉकिंग हार्डवेयर कितना तेज़ और कितना समानांतर है। आधुनिक हाई-एंड x86 इंटेल प्रोसेसर पर, पेज वॉकिंग हार्डवेयर सामान्य रूप से बहुत मजबूत होता है: कम से कम 2 समानांतर पृष्ठ वॉकर होते हैं, एक पेज वॉक निरंतर निष्पादन के साथ समवर्ती रूप से हो सकता है, और हार्डवेयर प्रीफ़ेटिंग एक पेज वॉक को ट्रिगर कर सकता है। इसलिए एक स्ट्रीमिंग रीड लोड पर टीएलबी प्रभाव काफी कम है - और ऐसा लोड अक्सर पृष्ठ आकार की परवाह किए बिना समान रूप से प्रदर्शन करेगा। अन्य हार्डवेयर आमतौर पर बहुत बदतर है, हालांकि!
read () इन नुकसानों से बचा जाता है
read()
Syscall है, जो आम तौर पर क्या underlies "ब्लॉक पढ़ा" प्रकार कॉल C, C ++ में, उदाहरण के लिए की पेशकश की और अन्य भाषाओं एक प्राथमिक नुकसान है कि हर किसी की अच्छी तरह से वाकिफ हैं:
read()
एन बाइट्स के प्रत्येक कॉल को कर्नेल से उपयोगकर्ता स्थान पर एन बाइट्स को कॉपी करना होगा।
दूसरी ओर, यह ऊपर की अधिकांश लागतों से बचता है - आपको उपयोगकर्ता स्थान में 25 मिलियन 4K पृष्ठों में मैप करने की आवश्यकता नहीं है। आप आमतौर पर malloc
यूजर स्पेस में सिंगल बफर छोटे बफर, और अपने सभी read
कॉल के लिए बार-बार उपयोग कर सकते हैं। कर्नेल की तरफ, 4K पेज या TLB के साथ लगभग कोई समस्या नहीं है क्योंकि सभी RAM में आमतौर पर कुछ बहुत बड़े पेज (जैसे x86 पर 1 जीबी पेज) का उपयोग करके रैखिक रूप से मैप किया जाता है, इसलिए पेज कैश में अंतर्निहित पेज कवर होते हैं बहुत ही कुशलता से कर्नेल स्पेस में।
तो मूल रूप से आपके पास यह निर्धारित करने के लिए निम्नलिखित तुलना है कि एक बड़ी फ़ाइल के एक एकल रीड के लिए तेज़ क्या है:
क्या mmap
उपयोग के द्वारा निहित कर्नेल से उपयोगकर्ता स्थान पर फ़ाइल सामग्री की प्रतिलिपि बनाने के प्रति-बाइट कार्य की तुलना में दृष्टिकोण से अधिक प्रति-पृष्ठ कार्य निहित है read()
?
कई प्रणालियों पर, वे वास्तव में लगभग संतुलित होते हैं। ध्यान दें कि हार्डवेयर और ओएस स्टैक की पूरी तरह से अलग विशेषताओं के साथ प्रत्येक एक तराजू।
विशेष रूप से, mmap
दृष्टिकोण अपेक्षाकृत तेज हो जाता है जब:
- ओएस में तेजी से मामूली-दोष से निपटने और विशेष रूप से मामूली-गलती bulking अनुकूलन जैसे गलती-आसपास है।
- OS का एक अच्छा
MAP_POPULATE
कार्यान्वयन है जो कुशलता से बड़े मानचित्रों को संसाधित कर सकता है, उदाहरण के लिए, अंतर्निहित पृष्ठ भौतिक स्मृति में सन्निहित हैं।
- हार्डवेयर में मजबूत पेज ट्रांसलेशन का प्रदर्शन होता है, जैसे कि बड़े टीएलबी, तेज दूसरे स्तर के टीएलबी, तेज और समानांतर पेज-वॉकर, अनुवाद के साथ अच्छा प्रीफैच इंटरैक्शन।
... जबकि read()
दृष्टिकोण अपेक्षाकृत तेज हो जाता है जब:
read()
Syscall अच्छा प्रतिलिपि प्रदर्शन है। जैसे, copy_to_user
कर्नेल की तरफ अच्छा प्रदर्शन।
- कर्नेल में हार्डवेयर समर्थन के साथ केवल कुछ बड़े पृष्ठों का उपयोग करके, मेमोरी को मैप करने का एक कुशल (उपयोगकर्ता के सापेक्ष) तरीका है।
- कर्नेल में तेज syscalls और syscalls के चारों ओर कर्नेल TLB प्रविष्टियाँ रखने का एक तरीका है।
हार्डवेयर कारकों से ऊपर अलग-अलग हो बेतहाशा भी एक ही परिवार में, विभिन्न प्लेटफार्मों भर में (जैसे, 86 पीढ़ियों और विशेष रूप से बाजार खंडों के भीतर) और निश्चित रूप से आर्किटेक्चर भर में (जैसे, एआरएम 86 बनाम पीपीसी बनाम)।
ओएस कारक दोनों पक्षों पर विभिन्न सुधारों के साथ-साथ एक दृष्टिकोण या दूसरे के लिए सापेक्ष गति में बड़े उछाल के कारण बदलते रहते हैं। एक हालिया सूची में शामिल हैं:
- ऊपर वर्णित गलती-आस-पास का जोड़, जो वास्तव में
mmap
मामले के बिना मदद करता है MAP_POPULATE
।
- तेजी से पथ के
copy_to_user
तरीकों को जोड़ना arch/x86/lib/copy_user_64.S
, उदाहरण के लिए, REP MOVQ
जब यह तेज होता है, जो वास्तव में read()
मामले में मदद करता है।
स्पेक्टर और मेल्टडाउन के बाद अपडेट करें
स्पेक्टर और मेल्टडाउन कमजोरियों के लिए मितव्ययिता ने सिस्टम कॉल की लागत में काफी वृद्धि की। मेरे द्वारा मापी जाने वाली प्रणालियों पर, "डू नथिंग" सिस्टम कॉल की लागत (जो सिस्टम कॉल के शुद्ध ओवरहेड का अनुमान है, कॉल के द्वारा किए गए किसी भी वास्तविक कार्य के अलावा) एक विशिष्ट पर लगभग 100 ns से चली गई आधुनिक लिनक्स सिस्टम लगभग s०० एन.एस. इसके अलावा, आपके सिस्टम के आधार पर, विशेष रूप से मेल्टडाउन के लिए पेज-टेबल आइसोलेशन फिक्स टीएलबी प्रविष्टियों को पुनः लोड करने की आवश्यकता के कारण डायरेक्ट सिस्टम कॉल लागत के अलावा अतिरिक्त डाउनस्ट्रीम प्रभाव हो सकता है।
यह सभी read()
आधारित विधियों की तुलना में आधारित विधियों के लिए एक सापेक्ष नुकसान है mmap
, क्योंकि read()
विधियों को प्रत्येक "बफर आकार" के डेटा के लिए एक सिस्टम कॉल करना होगा। आप इस लागत को बढ़ाने के लिए बफर आकार में मनमाने ढंग से वृद्धि नहीं कर सकते हैं क्योंकि आमतौर पर बड़े बफर का उपयोग करने से आप L1 के आकार से अधिक खराब हो जाते हैं और इसलिए लगातार कैश मिसेज पीड़ित हैं।
दूसरी ओर, mmap
आप MAP_POPULATE
केवल एक ही सिस्टम कॉल की कीमत पर, मेमोरी के एक बड़े क्षेत्र में और कुशलता से इसे एक्सेस कर सकते हैं।
1 इस अधिक-या-कम में वह मामला भी शामिल है जहां फ़ाइल को शुरू करने के लिए पूरी तरह से कैश नहीं किया गया था, लेकिन जहां ओएस रीड-फॉरवर्ड काफी अच्छा है ताकि यह दिखाई दे (इसलिए, पृष्ठ आमतौर पर आपके द्वारा कैश किया जाता है यह चाहता हूँ)। हालांकि यह एक सूक्ष्म मुद्दा है क्योंकि जिस तरह से पढ़ने-आगे काम करता है वह अक्सर बीच mmap
और read
कॉल में काफी भिन्न होता है , और इसे 2 में वर्णित "सलाह" कॉल द्वारा आगे समायोजित किया जा सकता है ।
2 ... क्योंकि यदि फ़ाइल कैश नहीं की जाती है, तो आपका व्यवहार IO चिंताओं पर पूरी तरह से हावी होने वाला है, जिसमें अंतर्निहित हार्डवेयर में आपकी पहुंच पैटर्न के प्रति सहानुभूति कितनी है - और इस तरह की पहुंच सुनिश्चित करने में आपका पूरा प्रयास उतना ही सहानुभूतिपूर्ण होना चाहिए जितना कि संभव है, उदाहरण के लिए madvise
या fadvise
कॉल के उपयोग के माध्यम से (और जो भी अनुप्रयोग स्तर में परिवर्तन आप पहुंच पैटर्न में सुधार कर सकते हैं)।
3 आप इसके चारों ओर मिल सकते हैं, उदाहरण के लिए, mmap
छोटे आकार की खिड़कियों में क्रमिक रूप से आईएनजी द्वारा , 100 एमबी।
4 वास्तव में, यह MAP_POPULATE
दृष्टिकोण को बदल देता है (कम से कम कुछ हार्डवेयर / ओएस संयोजन) केवल इसका उपयोग न करने की तुलना में थोड़ा तेज, शायद इसलिए कि कर्नेल गलती का उपयोग कर रहा है - इसलिए मामूली दोषों की वास्तविक संख्या 16 के एक कारक से कम हो जाती है या ऐसा।
mmap()
2-6 बार, syscalls का उपयोग कर जैसे की तुलना में तेजी हैread()
।