如何快速读取字节从内存映射文件在.net
本文关键字:映射 文件 net 内存 何快速 读取 字节 | 更新日期: 2023-09-27 18:12:58
在某些情况下,MemoryMappedViewAccessor
类只是不能有效地读取字节;我们得到的最好的是通用的ReadArray<byte>
,它是所有结构的路由,当你只需要字节时,涉及几个不必要的步骤。
可以使用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();
}
查看此bug报告:无法确定MemoryMappedViewAccessor使用的内部偏移量-使SafeMemoryMappedViewHandle属性不可用。
摘自报告:
MemoryMappedViewAccessor有一个SafeMemoryMappedViewHandle属性,它返回被MemoryMappedView内部使用的ViewHandle,但是没有任何属性返回被MemoryMappedView使用的偏移量。
由于MemoryMappedView是对齐MemoryMappedFile.CreateViewAccessor(offset,size)中请求的偏移量的页面,因此不可能在不知道偏移量的情况下使用SafeMemoryMappedViewHandle进行任何有用的操作。
请注意,我们实际上想做的是使用AcquirePointer(ref byte* pointer)方法来允许一些快速的基于指针的(可能是非托管的)代码运行。我们可以让指针与页面对齐,但必须有可能找出与原始请求地址的偏移量。
我知道这是一个已经回答过的老问题,但我想补充我的两点意见。
我用接受的答案(使用不安全的代码)和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:
- 483 ms
- 501 ms
- 490 ms
- 531 ms
- 517 ms
- 523 ms
从少量的测试来看,MemoryMappedViewStream
具有非常轻微的优势。考虑到这一点,我将选择MemoryMappedViewStream
。
这个解决方案的一个安全版本是:
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);
我已经测试过了,它确实有效。我不能评论它的性能,或者如果它是最好的整体解决方案,只是它的工作。
只是想与long l_offset共享一个版本(因此可以读取'写入大于int32 max大小的文件):
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;
}