2011-10-31 29 views
19

Trong một số trường hợp, lớp MemoryMappedViewAccessor chỉ không cắt nó để đọc byte hiệu quả; tốt nhất chúng tôi nhận được là chung ReadArray<byte> mà nó tuyến đường cho tất cả các cấu trúc và liên quan đến một số bước không cần thiết khi bạn chỉ cần byte.Làm thế nào tôi có thể đọc nhanh byte từ một tệp ánh xạ bộ nhớ trong .NET?

Có thể sử dụng MemoryMappedViewStream, nhưng vì nó dựa trên số Stream bạn cần phải tìm đúng vị trí đầu tiên, và sau đó chính thao tác đọc có nhiều bước không cần thiết hơn.

Có cách nhanh, hiệu suất cao để đọc một mảng byte từ tệp ánh xạ bộ nhớ trong .NET không, vì nó chỉ là một vùng cụ thể của không gian địa chỉ để đọc?

Trả lời

27

Giải pháp này yêu cầu mã không an toàn (biên dịch với chuyển đổi /unsafe), nhưng lấy trực tiếp con trỏ đến bộ nhớ; sau đó Marshal.Copy có thể được sử dụng. Điều này là nhanh hơn nhiều so với các phương thức được cung cấp bởi .NET framework.

// 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

Bạn nên sử dụng Khối thực thi quan trọng và thử cuối cùng để đảm bảo rằng ReleasePointer chạy ngay cả khi Marshal.Copy ném ngoại lệ. –

+3

Câu trả lời hay =) Thật vậy, lược tả cho thấy trình bao bọc được quản lý chậm hơn 30 lần so với sử dụng một con trỏ không an toàn để truy cập vào bộ nhớ được ánh xạ. –

+2

@MattHowells Tôi đồng ý. Tôi đọc rằng CER có thể ảnh hưởng đến hiệu suất, nhưng nó có vẻ cẩu thả (trong một bài kiểm tra kiểm soát ít nhất). Bất kể hàm ý hiệu suất nào, đó là mẫu sử dụng chính xác như được mô tả trong phần "nhận xét" ở đây; https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.safebuffer.acquirepointer(v=vs.110).aspx – LaFleur

2

Xem báo cáo lỗi này: No way to determine internal offset used by MemoryMappedViewAccessor - Makes SafeMemoryMappedViewHandle property unusable.

Từ báo cáo:

MemoryMappedViewAccessor có một tài sản SafeMemoryMappedViewHandle, mà trả về ViewHandle đang được sử dụng trong nội bộ bởi MemoryMappedView, nhưng không có bất cứ tài sản để trả về offset được sử dụng bởi MemoryMappedView.

Khi MemoryMappedView là trang căn chỉnh độ lệch được yêu cầu trong MemoryMappedFile.CreateViewAccessor (bù trừ, kích thước), không thể sử dụng SafeMemoryMappedViewHandle cho bất kỳ thứ gì hữu ích mà không biết bù đắp.

Lưu ý rằng những gì chúng tôi thực sự muốn làm là sử dụng phương pháp AcquirePointer (ref byte * pointer) để cho phép một số con trỏ nhanh (có thể không được quản lý) mã chạy. Chúng tôi đồng ý với con trỏ đang được căn chỉnh trang, nhưng nó phải có thể tìm ra những gì bù đắp từ địa chỉ được yêu cầu ban đầu.

+0

Có vẻ ngớ ngẩn .. nếu bạn kiểm soát chế độ xem, bạn không cần .NET để cho bạn biết giá trị offset, vì bạn đã chỉ định nó. (Đó là những gì tôi làm: '_view' là một người truy cập tại offset 0) –

+0

Fwiw, mã này cũng đã được thử nghiệm căng thẳng đến chết [hàng tỷ cuộc gọi, hàng nghìn MMF khác nhau] trên một số máy –

+0

Hiện nay hàng trăm tỷ cuộc gọi, và hàng trăm nghìn MMF. Lỗi này không xảy ra với mã của tôi;) –

1

Một phiên bản an toàn của giải pháp này là:

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); 

Tôi đã thử nghiệm này, nó làm việc. Tôi không thể bình luận về hiệu suất của nó hoặc nếu đó là giải pháp tổng thể NHẤT chỉ là nó hoạt động.

+1

Thật tuyệt, có chắc chắn tránh sử dụng con trỏ :) 'ReadArray ' là nhiều, chậm hơn nhiều, tuy nhiên –

0

Tôi biết đây là câu hỏi cũ đã được trả lời nhưng tôi muốn thêm hai xu của mình.

Tôi đã chạy thử nghiệm với cả câu trả lời được chấp nhận (sử dụng mã không an toàn) và với phương pháp MemoryMappedViewStream để đọc mảng byte 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"); 
      } 
     } 

tôi chạy thử nghiệm 3 lần với mỗi cách tiếp cận và nhận được những thời điểm sau.

MemoryMappedViewStream:

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

phương pháp không an toàn

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

Từ lượng nhỏ thử nghiệm nó trông giống như MemoryMappedViewStreamrất nhẹ lợi thế. Với ý nghĩ đó cho bất cứ ai đọc bài đăng này xuống đường tôi sẽ đi với MemoryMappedViewStream.

Các vấn đề liên quan