如何从.NET中的内存映射文件中快速读取字节?

在某些情况下, MemoryMappedViewAccessor类不会为了有效读取字节而剪切它; 我们得到的最好的是通用的ReadArray ,它是所有结构的路由,当你需要字节时涉及几个不必要的步骤。

可以使用MemoryMappedViewStream ,但由于它基于Stream您需要首先寻找正确的位置,然后读取操作本身有许多不必要的步骤。

是否有快速,高性能的方法从.NET中的内存映射文件读取字节数组,因为它应该只是地址空间的特定区域来读取?

这个解决方案需要不安全的代码(使用/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(); } 

请参阅此错误报告: 无法确定MemoryMappedViewAccessor使用的内部偏移量 – 使SafeMemoryMappedViewHandle属性不可用。

从报告中:

MemoryMappedViewAccessor具有SafeMemoryMappedViewHandle属性,该属性返回由MemoryMappedView在内部使用的ViewHandle,但没有任何属性可返回MemoryMappedView使用的偏移量。

由于MemoryMappedView是页面对齐MemoryMappedFile.CreateViewAccessor(offset,size)中请求的偏移量,因此在不知道偏移量的情况下,无法使用SafeMemoryMappedViewHandle进行任何有用的操作。

请注意,我们实际想要做的是使用AcquirePointer(ref byte * pointer)方法来允许运行一些基于快速指针的(可能是非托管的)代码。 我们可以将指针对齐页面,但必须能够找出原始请求地址的偏移量。

该解决方案的安全版本是:

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

我测试了这个,它确实有效。 我不能评论它的性能,或者它是最好的整体解决方案,只是它的工作原理。

我知道这是一个较旧的问题已经得到解答,但我想补充两分钱。

我使用接受的答案(使用不安全的代码)和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