2011-10-31 56 views
19

在某些情況下,MemoryMappedViewAccessor類不會削減它以便有效地讀取字節;我們得到的最好的是通用的ReadArray<byte>,它是所有結構的路徑,當你只需要字節時就涉及幾個不必要的步驟。如何快速從.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

您應該使用一個關鍵的執行塊和嘗試,終於以確保ReleasePointer運行,即使在Marshal.Copy拋出異常。 –

+3

好答案=)確實,分析顯示託管包裝比使用不安全指針訪問映射內存慢30倍。 – 2013-04-02 00:01:39

+2

@MattHowells我同意。我讀過CER可能會影響性能,但它似乎可以忽略不計(至少在一個受控測試中)。無論性能如何,它都是正確的使用模式,正如此處的「備註」中所述; 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使用的偏移量。

由於MemoryMappedView是頁面對齊MemoryMappedFile.CreateViewAccessor(offset,size)中所請求的偏移量,因此不可能在不知道偏移量的情況下使用SafeMemoryMappedViewHandle作爲任何有用的值。

請注意,我們真正想要做的是使用AcquirePointer(ref byte * pointer)方法來允許一些基於快速指針的(可能是非託管的)代碼運行。我們確定指針是頁面對齊的,但是必須能夠找出最初請求的地址的偏移量。

+0

看來愚蠢..如果您在視圖中的控制的時候,你不需要.NET來告訴你偏移量,因爲你指定了它。 (這是我做的:'_view'是在偏移0的訪問) –

+0

FWIW,這段代碼也已經壓力測試,以死亡[十億電話,數千種不同的貨幣市場基金的]在幾臺機器 –

+0

現在的數十億電話幾百個,和數十萬個MMF。這個錯誤不會發生在我的代碼中;) –

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

我知道這是一個已經回答的老問題,但我想補充我的兩分錢。

我使用接受的答案(使用不安全的代碼)和MemoryMappedViewStream方法來讀取一個200MB字節數組,進行了測試。

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