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

时间:2011-10-31 15:55:15

标签: c# .net memory-mapped-files

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

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

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

4 个答案:

答案 0 :(得分:30)

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

答案 1 :(得分:2)

请参阅此错误报告:No way to determine internal offset used by MemoryMappedViewAccessor - Makes SafeMemoryMappedViewHandle property unusable.

来自报告:

  

MemoryMappedViewAccessor有一个SafeMemoryMappedViewHandle属性,它返回MemoryMappedView内部使用的ViewHandle,但没有任何属性可以返回MemoryMappedView使用的偏移量。

     

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

     

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

答案 2 :(得分: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);

我测试了这个,它确实有效。我无法评论它的表现,或者它是否是最好的整体解决方案,只是它有效。

答案 3 :(得分:1)

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

我使用接受的答案(使用不安全的代码)和MemoryMappedViewStream方法运行测试,以读取200MB字节数组。

<强> MemoryMappedViewStream

describe('Overall Test Suite',function() {
 var expectedResult; //global variable for all 'it' blocks inside suite.
 it('Should navigate to webpage..', function(){
    log.info("Test is being executed..");
    login.goTo(parameters.url);
    login.login(parameters.username, parameters.password);
  });

it('Executing Queries', function(){
    expect(helper.executeSelectQuery(query_select)).toBeEqual(expectedResult);//let jasmine resolve the promise implicitly
 });
});

我每次接近测试3次并接受以下时间。

MemoryMappedViewStream:

  1. 483ms
  2. 501ms
  3. 490ms
  4. 不安全的方法

    1. 531ms
    2. 517ms
    3. 523ms
    4. 从少量测试来看, 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" ); } } 具有非常轻微的优势。考虑到这一点,对于那些在路上阅读这篇文章的人,我会选择MemoryMappedViewStream