如何在.NET中快速从内存Map文件中读取字节?

s1ag04yj  于 2022-11-26  发布在  .NET
关注(0)|答案(5)|浏览(131)

在某些情况下,MemoryMappedViewAccessor类不能有效地阅读字节;我们得到的最好的是泛型ReadArray<byte>,它是所有结构体的路由,当你只需要字节时,它涉及到几个不必要的步骤。
可以使用MemoryMappedViewStream,但因为它基于Stream,所以您需要首先查找到正确的位置,然后读取操作本身会有许多不必要的步骤。
在.NET中,有没有一种快速、高性能的方法来从内存Map文件中读取字节数组,假定它应该只是地址空间中要读取的特定区域?

k10s72fa

k10s72fa1#

这个解决方案需要unsafe代码(用/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();
    }
r6vfmomb

r6vfmomb2#

请参阅此错误报告:无法确定MemoryMappedViewAccessor使用的内部偏移量-使SafeMemoryMappedViewHandle属性不可用。
从报告中可以看出:
MemoryMappedViewAccessor具有SafeMemoryMappedViewHandle属性,该属性返回由MemoryMappedView在内部使用的ViewHandle,但没有任何属性返回由MemoryMappedView使用的偏移量。
由于MemoryMappedView正在对MemoryMappedFile.CreateViewAccessor(offset,size)中请求的偏移量进行页面对齐,因此在不知道偏移量的情况下,无法使用SafeMemoryMappedViewHandle进行任何有用的操作。
注意,我们实际上要做的是使用AcquirePointer(ref byte* pointer)方法来允许运行一些基于快速指针的(可能是非托管的)代码。我们可以将指针页面对齐,但必须能够找出从最初请求的地址的偏移量。

1cosmwyk

1cosmwyk3#

我知道这是一个已经回答过的老问题,但我想补充我的两分钱。
我使用接受的答案(使用不安全代码)和MemoryMappedViewStream方法运行了一个测试,以阅读200MB字节数组。

内存Map视图流

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次测试,并收到了以下时间。
内存Map视图流:

  1. 483毫秒
  2. 501毫秒
  3. 490毫秒
    不安全的方法
  4. 531毫秒
  5. 517毫秒
  6. 523毫秒
    从少量的测试来看,MemoryMappedViewStream似乎有一个 * 非常轻微 * 的优势。考虑到这一点,任何人阅读这篇文章的道路上,我会去与MemoryMappedViewStream
euoag5mw

euoag5mw4#

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

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

我已经测试过了,它确实有效。我不能评论它的性能,或者它是否是最好的整体解决方案,只是它的工作。

ercv8c1e

ercv8c1e5#

只想共享一个longl_offset的版本(这样就可以读取\写入大于int32最大大小的文件):

public static unsafe byte[] ReadBytes(long l_offset, int i_read_buf_size, MemoryMappedViewAccessor mmva)
    {
        byte[] arr = new byte[i_read_buf_size];
        byte* ptr = (byte*)0;
        mmva.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
        IntPtr p = new(ptr);
        Marshal.Copy(new IntPtr(p.ToInt64() + l_offset), arr, 0, i_read_buf_size);
        mmva.SafeMemoryMappedViewHandle.ReleasePointer();
        return arr;
    }

相关问题