2011-10-31 11 views
19

कुछ स्थितियों में MemoryMappedViewAccessor कक्षा बस इसे बाइट्स को प्रभावी ढंग से पढ़ने के लिए कट नहीं करती है; सबसे अच्छा हमें मिलता है जेनेरिक ReadArray<byte> जो यह सभी structs के लिए मार्ग है और आपको केवल बाइट्स की आवश्यकता होने पर कई अनावश्यक कदम शामिल हैं।मैं .NET में मेमोरी मैप की गई फ़ाइल से बाइट्स को तुरंत कैसे पढ़ सकता हूं?

MemoryMappedViewStream का उपयोग करना संभव है, लेकिन क्योंकि यह Stream पर आधारित है, आपको पहले सही स्थिति की तलाश करनी है, और फिर पढ़ने के ऑपरेशन में कई और अनावश्यक कदम हैं।

क्या .NET में मेमोरी-मैप की गई फ़ाइल से बाइट्स की सरणी पढ़ने के लिए एक त्वरित, उच्च-प्रदर्शन तरीका है, यह देखते हुए कि यह केवल पढ़ने के लिए पता स्थान का एक विशेष क्षेत्र होना चाहिए?

उत्तर

27

इस समाधान के लिए असुरक्षित कोड की आवश्यकता है (/unsafe स्विच के साथ संकलित), लेकिन सीधे मेमोरी के लिए एक सूचक को पकड़ता है; तो Marshal.Copy का उपयोग किया जा सकता है। यह .NET ढांचे द्वारा प्रदान की गई विधियों की तुलना में बहुत तेज़ है।

// assumes part of a class where _view is a MemoryMappedViewAccessor object 

    public unsafe byte[] ReadBytes(int offset, int num) 
    { 
     byte[] arr = new byte[num]; 
     byte *ptr = (byte*)0; 
     this._view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr); 
     Marshal.Copy(IntPtr.Add(new IntPtr(ptr), offset), arr, 0, num); 
     this._view.SafeMemoryMappedViewHandle.ReleasePointer(); 
     return arr; 
    } 

    public unsafe void WriteBytes(int offset, byte[] data) 
    { 
     byte* ptr = (byte*)0; 
     this._view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr); 
     Marshal.Copy(data, 0, IntPtr.Add(new IntPtr(ptr), offset), data.Length); 
     this._view.SafeMemoryMappedViewHandle.ReleasePointer(); 
    } 
+3

आपको एक गंभीर निष्पादन ब्लॉक का उपयोग करना चाहिए और यह सुनिश्चित करने के लिए प्रयास करना चाहिए कि रिलीज पॉइंटर भी मार्शल के रूप में चलता है। कॉपी एक अपवाद फेंकता है। –

+3

अच्छा उत्तर =) वास्तव में, प्रोफाइलिंग दिखाता है कि प्रबंधित रैपर मैप किए गए मेमोरी तक पहुंचने के लिए एक असुरक्षित पॉइंटर का उपयोग करने से 30x गुना धीमा है। –

+2

@ मैटहोवेल्स मैं सहमत हूं। मैंने पढ़ा है कि सीईआर प्रदर्शन को प्रभावित कर सकता है, लेकिन यह लापरवाही लगता है (कम से कम एक नियंत्रित परीक्षण में)। प्रदर्शन प्रभावों के बावजूद यह "उपयोग" के तहत वर्णित सही उपयोग पैटर्न है; https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.safebuffer.acquirepointer(v=vs.110).aspx – LaFleur

2

इस बग रिपोर्ट देखें: No way to determine internal offset used by MemoryMappedViewAccessor - Makes SafeMemoryMappedViewHandle property unusable.

रिपोर्ट से:

MemoryMappedViewAccessor एक SafeMemoryMappedViewHandle संपत्ति है, जो ViewHandle रिटर्न MemoryMappedView द्वारा आंतरिक रूप से इस्तेमाल किया जा रहा है, लेकिन किसी भी संपत्ति नहीं है MemoryMappedView द्वारा उपयोग किए जा रहे ऑफ़सेट को वापस करने के लिए।

मेमोरीमैप्ड व्यू मेमोरीमैप्डफाइल में अनुरोध किए गए ऑफसेट को संरेखित करने वाला पृष्ठ है। क्रिएटिव्यूएक्सेसर (ऑफसेट, आकार) ऑफसेट को जानने के बिना सुरक्षित मेमोरीव्यूडहैंडल का उपयोग करना असंभव है।

ध्यान दें कि हम वास्तव में क्या करना चाहते हैं, AcquirePointer (रेफ बाइट * पॉइंटर) विधि का उपयोग कुछ तेज सूचक (संभावित रूप से अप्रबंधित) कोड चलाने के लिए अनुमति देने के लिए है। हम पॉइंटर पृष्ठ को गठबंधन करने के साथ ठीक हैं, लेकिन यह पता लगाना संभव है कि मूल रूप से अनुरोध किए गए पते से ऑफसेट क्या है।

+0

यह मूर्खतापूर्ण लगता है .. यदि आप दृश्य के नियंत्रण में हैं, आपको ऑफसेट बताने के लिए आपको .NET की आवश्यकता नहीं है, क्योंकि आपने इसे निर्दिष्ट किया है। (यही वह है जो मैं करता हूं: '_view' ऑफसेट पर एक एक्सेसर है 0) –

+0

Fwiw, इस कोड को कई मशीनों पर मौत [अरबों कॉल, हजारों विभिन्न एमएमएफ] पर तनाव-परीक्षण किया गया है –

+0

अब सैकड़ों अरबों कॉल, और सैकड़ों हजारों एमएमएफ। यह बग मेरे कोड के साथ नहीं होता है;) –

1

इस समाधान का एक सुरक्षित संस्करण है:

var file = MemoryMappedFile.CreateFromFile(...); 
var accessor = file.CreateViewAccessor(); 
var bytes = new byte[yourLength]; 

// assuming the string is at the start of the file 
// aka position: 0 
// https://msdn.microsoft.com/en-us/library/dd267761(v=vs.110).aspx 
accessor.ReadArray<byte>(
    position: 0,  // The number of bytes in the accessor at which to begin reading 
    array: bytes,  // The array to contain the structures read from the accessor 
    offset: 0,  // The index in `array` in which to place the first copied structure 
    count: yourLength // The number of structures of type T to read from the accessor. 
); 

var myString = Encoding.UTF8.GetString(bytes); 

मैं इस परीक्षण किया है, यह काम करता है। मैं इसके प्रदर्शन पर टिप्पणी नहीं कर सकता हूं या यदि यह सबसे अच्छा समग्र समाधान है जो यह काम करता है।

+1

द्वारा दिए गए पते पर पॉइंटरऑफसेट जोड़ें कूल, हाँ निश्चित रूप से पॉइंटर्स के उपयोग से बचाता है :) 'ReadArray 'बहुत धीमा है, हालांकि –

0

मुझे पता है कि यह एक पुराना सवाल है जिसका उत्तर दिया गया है लेकिन मैं अपने दो सेंट जोड़ना चाहता था।

मैंने 200 एमबी बाइट सरणी पढ़ने के लिए स्वीकृत उत्तर (असुरक्षित कोड का उपयोग करके) और मेमोरी मैप किए गए व्यूस्ट्रीम दृष्टिकोण के साथ एक परीक्षण चलाया।

MemoryMappedViewStream

 const int MMF_MAX_SIZE = 209_715_200; 
     var buffer = new byte[ MMF_VIEW_SIZE ]; 

     using(var mmf = MemoryMappedFile.OpenExisting("mmf1")) 
     using(var view = mmf.CreateViewStream(0, buffer.Length, MemoryMappedFileAccess.ReadWrite)) 
     { 
      if(view.CanRead) 
      { 
       Console.WriteLine("Begin read"); 
       sw.Start(); 
       view.Read(buffer, 0, MMF_MAX_SIZE); 
       sw.Stop(); 
       Console.WriteLine($"Read done - {sw.ElapsedMilliseconds}ms"); 
      } 
     } 

मैं परीक्षण प्रत्येक दृष्टिकोण के साथ 3 बार भाग गया और निम्नलिखित समय प्राप्त किया।

MemoryMappedViewStream:

  1. 483ms
  2. 501ms
  3. 490ms

असुरक्षित विधि

  1. 531ms
  2. 517ms
  3. 523ms

परीक्षण यह लग रहा है MemoryMappedViewStream एक बहुत मामूली लाभ दिया है की तरह की एक छोटी राशि से। सड़क के नीचे इस पोस्ट को पढ़ने वाले किसी के लिए इसे ध्यान में रखते हुए मैं MemoryMappedViewStream के साथ जाऊंगा।

संबंधित मुद्दे